@push.rocks/smartdb 1.0.1 → 2.1.1
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/.smartconfig.json +18 -4
- package/dist_rust/rustdb_linux_amd64 +0 -0
- package/dist_rust/rustdb_linux_arm64 +0 -0
- package/dist_ts/00_commitinfo_data.js +3 -3
- package/dist_ts/index.d.ts +1 -0
- package/dist_ts/ts_local/classes.localsmartdb.d.ts +5 -5
- package/dist_ts/ts_local/classes.localsmartdb.js +7 -9
- package/dist_ts/ts_local/plugins.d.ts +1 -2
- package/dist_ts/ts_local/plugins.js +3 -3
- package/dist_ts/ts_smartdb/index.d.ts +2 -24
- package/dist_ts/ts_smartdb/index.js +4 -29
- package/dist_ts/ts_smartdb/plugins.d.ts +2 -10
- package/dist_ts/ts_smartdb/plugins.js +3 -13
- package/dist_ts/ts_smartdb/rust-db-bridge.d.ts +122 -0
- package/dist_ts/ts_smartdb/rust-db-bridge.js +113 -0
- package/dist_ts/ts_smartdb/server/SmartdbServer.d.ts +39 -37
- package/dist_ts/ts_smartdb/server/SmartdbServer.js +87 -206
- package/dist_ts/ts_smartdb/server/index.d.ts +0 -4
- package/dist_ts/ts_smartdb/server/index.js +1 -5
- package/dist_ts_debugserver/bundled.d.ts +4 -0
- package/dist_ts_debugserver/bundled.js +12 -0
- package/dist_ts_debugserver/classes.debugserver.d.ts +36 -0
- package/dist_ts_debugserver/classes.debugserver.js +95 -0
- package/dist_ts_debugserver/index.d.ts +2 -0
- package/dist_ts_debugserver/index.js +2 -0
- package/dist_ts_debugserver/plugins.d.ts +2 -0
- package/dist_ts_debugserver/plugins.js +3 -0
- package/dist_ts_debugui/index.d.ts +2 -0
- package/dist_ts_debugui/index.js +2 -0
- package/dist_ts_debugui/plugins.d.ts +1 -0
- package/dist_ts_debugui/plugins.js +2 -0
- package/dist_ts_debugui/smartdb-debugui.d.ts +62 -0
- package/dist_ts_debugui/smartdb-debugui.js +1132 -0
- package/license +3 -1
- package/package.json +14 -13
- package/readme.md +209 -177
- package/ts/00_commitinfo_data.ts +2 -2
- package/ts/index.ts +11 -0
- package/ts/ts_local/classes.localsmartdb.ts +5 -6
- package/ts/ts_local/plugins.ts +1 -3
- package/ts/ts_smartdb/index.ts +14 -41
- package/ts/ts_smartdb/plugins.ts +2 -15
- package/ts/ts_smartdb/rust-db-bridge.ts +262 -0
- package/ts/ts_smartdb/server/SmartdbServer.ts +115 -246
- package/ts/ts_smartdb/server/index.ts +0 -7
- package/dist_ts/ts_smartdb/engine/AggregationEngine.d.ts +0 -66
- package/dist_ts/ts_smartdb/engine/AggregationEngine.js +0 -189
- package/dist_ts/ts_smartdb/engine/IndexEngine.d.ts +0 -97
- package/dist_ts/ts_smartdb/engine/IndexEngine.js +0 -678
- package/dist_ts/ts_smartdb/engine/QueryEngine.d.ts +0 -54
- package/dist_ts/ts_smartdb/engine/QueryEngine.js +0 -271
- package/dist_ts/ts_smartdb/engine/QueryPlanner.d.ts +0 -64
- package/dist_ts/ts_smartdb/engine/QueryPlanner.js +0 -308
- package/dist_ts/ts_smartdb/engine/SessionEngine.d.ts +0 -117
- package/dist_ts/ts_smartdb/engine/SessionEngine.js +0 -232
- package/dist_ts/ts_smartdb/engine/TransactionEngine.d.ts +0 -85
- package/dist_ts/ts_smartdb/engine/TransactionEngine.js +0 -287
- package/dist_ts/ts_smartdb/engine/UpdateEngine.d.ts +0 -47
- package/dist_ts/ts_smartdb/engine/UpdateEngine.js +0 -461
- package/dist_ts/ts_smartdb/errors/SmartdbErrors.d.ts +0 -100
- package/dist_ts/ts_smartdb/errors/SmartdbErrors.js +0 -155
- package/dist_ts/ts_smartdb/server/CommandRouter.d.ts +0 -87
- package/dist_ts/ts_smartdb/server/CommandRouter.js +0 -222
- package/dist_ts/ts_smartdb/server/WireProtocol.d.ts +0 -117
- package/dist_ts/ts_smartdb/server/WireProtocol.js +0 -298
- package/dist_ts/ts_smartdb/server/handlers/AdminHandler.d.ts +0 -100
- package/dist_ts/ts_smartdb/server/handlers/AdminHandler.js +0 -668
- package/dist_ts/ts_smartdb/server/handlers/AggregateHandler.d.ts +0 -31
- package/dist_ts/ts_smartdb/server/handlers/AggregateHandler.js +0 -277
- package/dist_ts/ts_smartdb/server/handlers/DeleteHandler.d.ts +0 -8
- package/dist_ts/ts_smartdb/server/handlers/DeleteHandler.js +0 -95
- package/dist_ts/ts_smartdb/server/handlers/FindHandler.d.ts +0 -31
- package/dist_ts/ts_smartdb/server/handlers/FindHandler.js +0 -291
- package/dist_ts/ts_smartdb/server/handlers/HelloHandler.d.ts +0 -11
- package/dist_ts/ts_smartdb/server/handlers/HelloHandler.js +0 -62
- package/dist_ts/ts_smartdb/server/handlers/IndexHandler.d.ts +0 -20
- package/dist_ts/ts_smartdb/server/handlers/IndexHandler.js +0 -183
- package/dist_ts/ts_smartdb/server/handlers/InsertHandler.d.ts +0 -8
- package/dist_ts/ts_smartdb/server/handlers/InsertHandler.js +0 -79
- package/dist_ts/ts_smartdb/server/handlers/UpdateHandler.d.ts +0 -24
- package/dist_ts/ts_smartdb/server/handlers/UpdateHandler.js +0 -296
- package/dist_ts/ts_smartdb/server/handlers/index.d.ts +0 -8
- package/dist_ts/ts_smartdb/server/handlers/index.js +0 -10
- package/dist_ts/ts_smartdb/storage/FileStorageAdapter.d.ts +0 -85
- package/dist_ts/ts_smartdb/storage/FileStorageAdapter.js +0 -465
- package/dist_ts/ts_smartdb/storage/IStorageAdapter.d.ts +0 -145
- package/dist_ts/ts_smartdb/storage/IStorageAdapter.js +0 -2
- package/dist_ts/ts_smartdb/storage/MemoryStorageAdapter.d.ts +0 -67
- package/dist_ts/ts_smartdb/storage/MemoryStorageAdapter.js +0 -378
- package/dist_ts/ts_smartdb/storage/OpLog.d.ts +0 -93
- package/dist_ts/ts_smartdb/storage/OpLog.js +0 -221
- package/dist_ts/ts_smartdb/storage/WAL.d.ts +0 -117
- package/dist_ts/ts_smartdb/storage/WAL.js +0 -286
- package/dist_ts/ts_smartdb/types/interfaces.d.ts +0 -363
- package/dist_ts/ts_smartdb/types/interfaces.js +0 -2
- package/dist_ts/ts_smartdb/utils/checksum.d.ts +0 -30
- package/dist_ts/ts_smartdb/utils/checksum.js +0 -77
- package/dist_ts/ts_smartdb/utils/index.d.ts +0 -1
- package/dist_ts/ts_smartdb/utils/index.js +0 -2
- package/ts/ts_smartdb/engine/AggregationEngine.ts +0 -283
- package/ts/ts_smartdb/engine/IndexEngine.ts +0 -798
- package/ts/ts_smartdb/engine/QueryEngine.ts +0 -301
- package/ts/ts_smartdb/engine/QueryPlanner.ts +0 -393
- package/ts/ts_smartdb/engine/SessionEngine.ts +0 -292
- package/ts/ts_smartdb/engine/TransactionEngine.ts +0 -351
- package/ts/ts_smartdb/engine/UpdateEngine.ts +0 -506
- package/ts/ts_smartdb/errors/SmartdbErrors.ts +0 -181
- package/ts/ts_smartdb/server/CommandRouter.ts +0 -289
- package/ts/ts_smartdb/server/WireProtocol.ts +0 -416
- package/ts/ts_smartdb/server/handlers/AdminHandler.ts +0 -719
- package/ts/ts_smartdb/server/handlers/AggregateHandler.ts +0 -342
- package/ts/ts_smartdb/server/handlers/DeleteHandler.ts +0 -115
- package/ts/ts_smartdb/server/handlers/FindHandler.ts +0 -330
- package/ts/ts_smartdb/server/handlers/HelloHandler.ts +0 -78
- package/ts/ts_smartdb/server/handlers/IndexHandler.ts +0 -207
- package/ts/ts_smartdb/server/handlers/InsertHandler.ts +0 -97
- package/ts/ts_smartdb/server/handlers/UpdateHandler.ts +0 -344
- package/ts/ts_smartdb/server/handlers/index.ts +0 -10
- package/ts/ts_smartdb/storage/FileStorageAdapter.ts +0 -562
- package/ts/ts_smartdb/storage/IStorageAdapter.ts +0 -208
- package/ts/ts_smartdb/storage/MemoryStorageAdapter.ts +0 -455
- package/ts/ts_smartdb/storage/OpLog.ts +0 -282
- package/ts/ts_smartdb/storage/WAL.ts +0 -375
- package/ts/ts_smartdb/types/interfaces.ts +0 -433
- package/ts/ts_smartdb/utils/checksum.ts +0 -88
- package/ts/ts_smartdb/utils/index.ts +0 -1
|
@@ -1,208 +0,0 @@
|
|
|
1
|
-
import type * as plugins from '../plugins.js';
|
|
2
|
-
import type { IStoredDocument, IOpLogEntry, Document } from '../types/interfaces.js';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Storage adapter interface for SmartDB
|
|
6
|
-
* Implementations can provide different storage backends (memory, file, etc.)
|
|
7
|
-
*/
|
|
8
|
-
export interface IStorageAdapter {
|
|
9
|
-
/**
|
|
10
|
-
* Initialize the storage adapter
|
|
11
|
-
*/
|
|
12
|
-
initialize(): Promise<void>;
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Close the storage adapter and release resources
|
|
16
|
-
*/
|
|
17
|
-
close(): Promise<void>;
|
|
18
|
-
|
|
19
|
-
// ============================================================================
|
|
20
|
-
// Database Operations
|
|
21
|
-
// ============================================================================
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* List all database names
|
|
25
|
-
*/
|
|
26
|
-
listDatabases(): Promise<string[]>;
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Create a database (typically lazy - just marks it as existing)
|
|
30
|
-
*/
|
|
31
|
-
createDatabase(dbName: string): Promise<void>;
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Drop a database and all its collections
|
|
35
|
-
*/
|
|
36
|
-
dropDatabase(dbName: string): Promise<boolean>;
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Check if a database exists
|
|
40
|
-
*/
|
|
41
|
-
databaseExists(dbName: string): Promise<boolean>;
|
|
42
|
-
|
|
43
|
-
// ============================================================================
|
|
44
|
-
// Collection Operations
|
|
45
|
-
// ============================================================================
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* List all collection names in a database
|
|
49
|
-
*/
|
|
50
|
-
listCollections(dbName: string): Promise<string[]>;
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Create a collection
|
|
54
|
-
*/
|
|
55
|
-
createCollection(dbName: string, collName: string): Promise<void>;
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Drop a collection
|
|
59
|
-
*/
|
|
60
|
-
dropCollection(dbName: string, collName: string): Promise<boolean>;
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Check if a collection exists
|
|
64
|
-
*/
|
|
65
|
-
collectionExists(dbName: string, collName: string): Promise<boolean>;
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* Rename a collection
|
|
69
|
-
*/
|
|
70
|
-
renameCollection(dbName: string, oldName: string, newName: string): Promise<void>;
|
|
71
|
-
|
|
72
|
-
// ============================================================================
|
|
73
|
-
// Document Operations
|
|
74
|
-
// ============================================================================
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* Insert a single document
|
|
78
|
-
* @returns The inserted document with _id
|
|
79
|
-
*/
|
|
80
|
-
insertOne(dbName: string, collName: string, doc: Document): Promise<IStoredDocument>;
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* Insert multiple documents
|
|
84
|
-
* @returns Array of inserted documents with _ids
|
|
85
|
-
*/
|
|
86
|
-
insertMany(dbName: string, collName: string, docs: Document[]): Promise<IStoredDocument[]>;
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* Find all documents in a collection
|
|
90
|
-
*/
|
|
91
|
-
findAll(dbName: string, collName: string): Promise<IStoredDocument[]>;
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* Find documents by a set of _id strings (hex format)
|
|
95
|
-
* Used for index-accelerated queries
|
|
96
|
-
*/
|
|
97
|
-
findByIds(dbName: string, collName: string, ids: Set<string>): Promise<IStoredDocument[]>;
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* Find a document by _id
|
|
101
|
-
*/
|
|
102
|
-
findById(dbName: string, collName: string, id: plugins.bson.ObjectId): Promise<IStoredDocument | null>;
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* Update a document by _id
|
|
106
|
-
* @returns true if document was updated
|
|
107
|
-
*/
|
|
108
|
-
updateById(dbName: string, collName: string, id: plugins.bson.ObjectId, doc: IStoredDocument): Promise<boolean>;
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* Delete a document by _id
|
|
112
|
-
* @returns true if document was deleted
|
|
113
|
-
*/
|
|
114
|
-
deleteById(dbName: string, collName: string, id: plugins.bson.ObjectId): Promise<boolean>;
|
|
115
|
-
|
|
116
|
-
/**
|
|
117
|
-
* Delete multiple documents by _id
|
|
118
|
-
* @returns Number of deleted documents
|
|
119
|
-
*/
|
|
120
|
-
deleteByIds(dbName: string, collName: string, ids: plugins.bson.ObjectId[]): Promise<number>;
|
|
121
|
-
|
|
122
|
-
/**
|
|
123
|
-
* Get the count of documents in a collection
|
|
124
|
-
*/
|
|
125
|
-
count(dbName: string, collName: string): Promise<number>;
|
|
126
|
-
|
|
127
|
-
// ============================================================================
|
|
128
|
-
// Index Operations
|
|
129
|
-
// ============================================================================
|
|
130
|
-
|
|
131
|
-
/**
|
|
132
|
-
* Store index metadata
|
|
133
|
-
*/
|
|
134
|
-
saveIndex(
|
|
135
|
-
dbName: string,
|
|
136
|
-
collName: string,
|
|
137
|
-
indexName: string,
|
|
138
|
-
indexSpec: { key: Record<string, any>; unique?: boolean; sparse?: boolean; expireAfterSeconds?: number }
|
|
139
|
-
): Promise<void>;
|
|
140
|
-
|
|
141
|
-
/**
|
|
142
|
-
* Get all index metadata for a collection
|
|
143
|
-
*/
|
|
144
|
-
getIndexes(dbName: string, collName: string): Promise<Array<{
|
|
145
|
-
name: string;
|
|
146
|
-
key: Record<string, any>;
|
|
147
|
-
unique?: boolean;
|
|
148
|
-
sparse?: boolean;
|
|
149
|
-
expireAfterSeconds?: number;
|
|
150
|
-
}>>;
|
|
151
|
-
|
|
152
|
-
/**
|
|
153
|
-
* Drop an index
|
|
154
|
-
*/
|
|
155
|
-
dropIndex(dbName: string, collName: string, indexName: string): Promise<boolean>;
|
|
156
|
-
|
|
157
|
-
// ============================================================================
|
|
158
|
-
// OpLog Operations (for change streams)
|
|
159
|
-
// ============================================================================
|
|
160
|
-
|
|
161
|
-
/**
|
|
162
|
-
* Append an operation to the oplog
|
|
163
|
-
*/
|
|
164
|
-
appendOpLog(entry: IOpLogEntry): Promise<void>;
|
|
165
|
-
|
|
166
|
-
/**
|
|
167
|
-
* Get oplog entries after a timestamp
|
|
168
|
-
*/
|
|
169
|
-
getOpLogAfter(ts: plugins.bson.Timestamp, limit?: number): Promise<IOpLogEntry[]>;
|
|
170
|
-
|
|
171
|
-
/**
|
|
172
|
-
* Get the latest oplog timestamp
|
|
173
|
-
*/
|
|
174
|
-
getLatestOpLogTimestamp(): Promise<plugins.bson.Timestamp | null>;
|
|
175
|
-
|
|
176
|
-
// ============================================================================
|
|
177
|
-
// Transaction Support
|
|
178
|
-
// ============================================================================
|
|
179
|
-
|
|
180
|
-
/**
|
|
181
|
-
* Create a snapshot of current data for transaction isolation
|
|
182
|
-
*/
|
|
183
|
-
createSnapshot(dbName: string, collName: string): Promise<IStoredDocument[]>;
|
|
184
|
-
|
|
185
|
-
/**
|
|
186
|
-
* Check if any documents have been modified since the snapshot
|
|
187
|
-
*/
|
|
188
|
-
hasConflicts(
|
|
189
|
-
dbName: string,
|
|
190
|
-
collName: string,
|
|
191
|
-
ids: plugins.bson.ObjectId[],
|
|
192
|
-
snapshotTime: plugins.bson.Timestamp
|
|
193
|
-
): Promise<boolean>;
|
|
194
|
-
|
|
195
|
-
// ============================================================================
|
|
196
|
-
// Persistence (optional, for MemoryStorageAdapter with file backup)
|
|
197
|
-
// ============================================================================
|
|
198
|
-
|
|
199
|
-
/**
|
|
200
|
-
* Persist current state to disk (if supported)
|
|
201
|
-
*/
|
|
202
|
-
persist?(): Promise<void>;
|
|
203
|
-
|
|
204
|
-
/**
|
|
205
|
-
* Load state from disk (if supported)
|
|
206
|
-
*/
|
|
207
|
-
restore?(): Promise<void>;
|
|
208
|
-
}
|
|
@@ -1,455 +0,0 @@
|
|
|
1
|
-
import * as plugins from '../plugins.js';
|
|
2
|
-
import type { IStorageAdapter } from './IStorageAdapter.js';
|
|
3
|
-
import type { IStoredDocument, IOpLogEntry, Document } from '../types/interfaces.js';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* In-memory storage adapter for SmartDB
|
|
7
|
-
* Optionally supports persistence to a file
|
|
8
|
-
*/
|
|
9
|
-
export class MemoryStorageAdapter implements IStorageAdapter {
|
|
10
|
-
// Database -> Collection -> Documents
|
|
11
|
-
private databases: Map<string, Map<string, Map<string, IStoredDocument>>> = new Map();
|
|
12
|
-
|
|
13
|
-
// Database -> Collection -> Indexes
|
|
14
|
-
private indexes: Map<string, Map<string, Array<{
|
|
15
|
-
name: string;
|
|
16
|
-
key: Record<string, any>;
|
|
17
|
-
unique?: boolean;
|
|
18
|
-
sparse?: boolean;
|
|
19
|
-
expireAfterSeconds?: number;
|
|
20
|
-
}>>> = new Map();
|
|
21
|
-
|
|
22
|
-
// OpLog entries
|
|
23
|
-
private opLog: IOpLogEntry[] = [];
|
|
24
|
-
private opLogCounter = 0;
|
|
25
|
-
|
|
26
|
-
// Persistence settings
|
|
27
|
-
private persistPath?: string;
|
|
28
|
-
private persistInterval?: ReturnType<typeof setInterval>;
|
|
29
|
-
private fs = new plugins.smartfs.SmartFs(new plugins.smartfs.SmartFsProviderNode());
|
|
30
|
-
|
|
31
|
-
constructor(options?: { persistPath?: string; persistIntervalMs?: number }) {
|
|
32
|
-
this.persistPath = options?.persistPath;
|
|
33
|
-
if (this.persistPath && options?.persistIntervalMs) {
|
|
34
|
-
this.persistInterval = setInterval(() => {
|
|
35
|
-
this.persist().catch(console.error);
|
|
36
|
-
}, options.persistIntervalMs);
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
async initialize(): Promise<void> {
|
|
41
|
-
if (this.persistPath) {
|
|
42
|
-
await this.restore();
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
async close(): Promise<void> {
|
|
47
|
-
if (this.persistInterval) {
|
|
48
|
-
clearInterval(this.persistInterval);
|
|
49
|
-
}
|
|
50
|
-
if (this.persistPath) {
|
|
51
|
-
await this.persist();
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// ============================================================================
|
|
56
|
-
// Database Operations
|
|
57
|
-
// ============================================================================
|
|
58
|
-
|
|
59
|
-
async listDatabases(): Promise<string[]> {
|
|
60
|
-
return Array.from(this.databases.keys());
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
async createDatabase(dbName: string): Promise<void> {
|
|
64
|
-
if (!this.databases.has(dbName)) {
|
|
65
|
-
this.databases.set(dbName, new Map());
|
|
66
|
-
this.indexes.set(dbName, new Map());
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
async dropDatabase(dbName: string): Promise<boolean> {
|
|
71
|
-
const existed = this.databases.has(dbName);
|
|
72
|
-
this.databases.delete(dbName);
|
|
73
|
-
this.indexes.delete(dbName);
|
|
74
|
-
return existed;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
async databaseExists(dbName: string): Promise<boolean> {
|
|
78
|
-
return this.databases.has(dbName);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
// ============================================================================
|
|
82
|
-
// Collection Operations
|
|
83
|
-
// ============================================================================
|
|
84
|
-
|
|
85
|
-
async listCollections(dbName: string): Promise<string[]> {
|
|
86
|
-
const db = this.databases.get(dbName);
|
|
87
|
-
return db ? Array.from(db.keys()) : [];
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
async createCollection(dbName: string, collName: string): Promise<void> {
|
|
91
|
-
await this.createDatabase(dbName);
|
|
92
|
-
const db = this.databases.get(dbName)!;
|
|
93
|
-
if (!db.has(collName)) {
|
|
94
|
-
db.set(collName, new Map());
|
|
95
|
-
// Initialize default _id index
|
|
96
|
-
const dbIndexes = this.indexes.get(dbName)!;
|
|
97
|
-
dbIndexes.set(collName, [{ name: '_id_', key: { _id: 1 }, unique: true }]);
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
async dropCollection(dbName: string, collName: string): Promise<boolean> {
|
|
102
|
-
const db = this.databases.get(dbName);
|
|
103
|
-
if (!db) return false;
|
|
104
|
-
const existed = db.has(collName);
|
|
105
|
-
db.delete(collName);
|
|
106
|
-
const dbIndexes = this.indexes.get(dbName);
|
|
107
|
-
if (dbIndexes) {
|
|
108
|
-
dbIndexes.delete(collName);
|
|
109
|
-
}
|
|
110
|
-
return existed;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
async collectionExists(dbName: string, collName: string): Promise<boolean> {
|
|
114
|
-
const db = this.databases.get(dbName);
|
|
115
|
-
return db ? db.has(collName) : false;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
async renameCollection(dbName: string, oldName: string, newName: string): Promise<void> {
|
|
119
|
-
const db = this.databases.get(dbName);
|
|
120
|
-
if (!db || !db.has(oldName)) {
|
|
121
|
-
throw new Error(`Collection ${oldName} not found`);
|
|
122
|
-
}
|
|
123
|
-
const collection = db.get(oldName)!;
|
|
124
|
-
db.set(newName, collection);
|
|
125
|
-
db.delete(oldName);
|
|
126
|
-
|
|
127
|
-
// Also rename indexes
|
|
128
|
-
const dbIndexes = this.indexes.get(dbName);
|
|
129
|
-
if (dbIndexes && dbIndexes.has(oldName)) {
|
|
130
|
-
const collIndexes = dbIndexes.get(oldName)!;
|
|
131
|
-
dbIndexes.set(newName, collIndexes);
|
|
132
|
-
dbIndexes.delete(oldName);
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
// ============================================================================
|
|
137
|
-
// Document Operations
|
|
138
|
-
// ============================================================================
|
|
139
|
-
|
|
140
|
-
private getCollection(dbName: string, collName: string): Map<string, IStoredDocument> {
|
|
141
|
-
const db = this.databases.get(dbName);
|
|
142
|
-
if (!db) {
|
|
143
|
-
throw new Error(`Database ${dbName} not found`);
|
|
144
|
-
}
|
|
145
|
-
const collection = db.get(collName);
|
|
146
|
-
if (!collection) {
|
|
147
|
-
throw new Error(`Collection ${collName} not found`);
|
|
148
|
-
}
|
|
149
|
-
return collection;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
private ensureCollection(dbName: string, collName: string): Map<string, IStoredDocument> {
|
|
153
|
-
if (!this.databases.has(dbName)) {
|
|
154
|
-
this.databases.set(dbName, new Map());
|
|
155
|
-
this.indexes.set(dbName, new Map());
|
|
156
|
-
}
|
|
157
|
-
const db = this.databases.get(dbName)!;
|
|
158
|
-
if (!db.has(collName)) {
|
|
159
|
-
db.set(collName, new Map());
|
|
160
|
-
const dbIndexes = this.indexes.get(dbName)!;
|
|
161
|
-
dbIndexes.set(collName, [{ name: '_id_', key: { _id: 1 }, unique: true }]);
|
|
162
|
-
}
|
|
163
|
-
return db.get(collName)!;
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
async insertOne(dbName: string, collName: string, doc: Document): Promise<IStoredDocument> {
|
|
167
|
-
const collection = this.ensureCollection(dbName, collName);
|
|
168
|
-
const storedDoc: IStoredDocument = {
|
|
169
|
-
...doc,
|
|
170
|
-
_id: doc._id instanceof plugins.bson.ObjectId ? doc._id : new plugins.bson.ObjectId(doc._id),
|
|
171
|
-
};
|
|
172
|
-
|
|
173
|
-
if (!storedDoc._id) {
|
|
174
|
-
storedDoc._id = new plugins.bson.ObjectId();
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
const idStr = storedDoc._id.toHexString();
|
|
178
|
-
if (collection.has(idStr)) {
|
|
179
|
-
throw new Error(`Duplicate key error: _id ${idStr}`);
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
collection.set(idStr, storedDoc);
|
|
183
|
-
return storedDoc;
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
async insertMany(dbName: string, collName: string, docs: Document[]): Promise<IStoredDocument[]> {
|
|
187
|
-
const results: IStoredDocument[] = [];
|
|
188
|
-
for (const doc of docs) {
|
|
189
|
-
results.push(await this.insertOne(dbName, collName, doc));
|
|
190
|
-
}
|
|
191
|
-
return results;
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
async findAll(dbName: string, collName: string): Promise<IStoredDocument[]> {
|
|
195
|
-
const collection = this.ensureCollection(dbName, collName);
|
|
196
|
-
return Array.from(collection.values());
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
async findByIds(dbName: string, collName: string, ids: Set<string>): Promise<IStoredDocument[]> {
|
|
200
|
-
const collection = this.ensureCollection(dbName, collName);
|
|
201
|
-
const results: IStoredDocument[] = [];
|
|
202
|
-
for (const id of ids) {
|
|
203
|
-
const doc = collection.get(id);
|
|
204
|
-
if (doc) {
|
|
205
|
-
results.push(doc);
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
return results;
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
async findById(dbName: string, collName: string, id: plugins.bson.ObjectId): Promise<IStoredDocument | null> {
|
|
212
|
-
const collection = this.ensureCollection(dbName, collName);
|
|
213
|
-
return collection.get(id.toHexString()) || null;
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
async updateById(dbName: string, collName: string, id: plugins.bson.ObjectId, doc: IStoredDocument): Promise<boolean> {
|
|
217
|
-
const collection = this.ensureCollection(dbName, collName);
|
|
218
|
-
const idStr = id.toHexString();
|
|
219
|
-
if (!collection.has(idStr)) {
|
|
220
|
-
return false;
|
|
221
|
-
}
|
|
222
|
-
collection.set(idStr, doc);
|
|
223
|
-
return true;
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
async deleteById(dbName: string, collName: string, id: plugins.bson.ObjectId): Promise<boolean> {
|
|
227
|
-
const collection = this.ensureCollection(dbName, collName);
|
|
228
|
-
return collection.delete(id.toHexString());
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
async deleteByIds(dbName: string, collName: string, ids: plugins.bson.ObjectId[]): Promise<number> {
|
|
232
|
-
let count = 0;
|
|
233
|
-
for (const id of ids) {
|
|
234
|
-
if (await this.deleteById(dbName, collName, id)) {
|
|
235
|
-
count++;
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
return count;
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
async count(dbName: string, collName: string): Promise<number> {
|
|
242
|
-
const collection = this.ensureCollection(dbName, collName);
|
|
243
|
-
return collection.size;
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
// ============================================================================
|
|
247
|
-
// Index Operations
|
|
248
|
-
// ============================================================================
|
|
249
|
-
|
|
250
|
-
async saveIndex(
|
|
251
|
-
dbName: string,
|
|
252
|
-
collName: string,
|
|
253
|
-
indexName: string,
|
|
254
|
-
indexSpec: { key: Record<string, any>; unique?: boolean; sparse?: boolean; expireAfterSeconds?: number }
|
|
255
|
-
): Promise<void> {
|
|
256
|
-
await this.createCollection(dbName, collName);
|
|
257
|
-
const dbIndexes = this.indexes.get(dbName)!;
|
|
258
|
-
let collIndexes = dbIndexes.get(collName);
|
|
259
|
-
if (!collIndexes) {
|
|
260
|
-
collIndexes = [{ name: '_id_', key: { _id: 1 }, unique: true }];
|
|
261
|
-
dbIndexes.set(collName, collIndexes);
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
// Check if index already exists
|
|
265
|
-
const existingIndex = collIndexes.findIndex(i => i.name === indexName);
|
|
266
|
-
if (existingIndex >= 0) {
|
|
267
|
-
collIndexes[existingIndex] = { name: indexName, ...indexSpec };
|
|
268
|
-
} else {
|
|
269
|
-
collIndexes.push({ name: indexName, ...indexSpec });
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
async getIndexes(dbName: string, collName: string): Promise<Array<{
|
|
274
|
-
name: string;
|
|
275
|
-
key: Record<string, any>;
|
|
276
|
-
unique?: boolean;
|
|
277
|
-
sparse?: boolean;
|
|
278
|
-
expireAfterSeconds?: number;
|
|
279
|
-
}>> {
|
|
280
|
-
const dbIndexes = this.indexes.get(dbName);
|
|
281
|
-
if (!dbIndexes) return [{ name: '_id_', key: { _id: 1 }, unique: true }];
|
|
282
|
-
const collIndexes = dbIndexes.get(collName);
|
|
283
|
-
return collIndexes || [{ name: '_id_', key: { _id: 1 }, unique: true }];
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
async dropIndex(dbName: string, collName: string, indexName: string): Promise<boolean> {
|
|
287
|
-
if (indexName === '_id_') {
|
|
288
|
-
throw new Error('Cannot drop _id index');
|
|
289
|
-
}
|
|
290
|
-
const dbIndexes = this.indexes.get(dbName);
|
|
291
|
-
if (!dbIndexes) return false;
|
|
292
|
-
const collIndexes = dbIndexes.get(collName);
|
|
293
|
-
if (!collIndexes) return false;
|
|
294
|
-
|
|
295
|
-
const idx = collIndexes.findIndex(i => i.name === indexName);
|
|
296
|
-
if (idx >= 0) {
|
|
297
|
-
collIndexes.splice(idx, 1);
|
|
298
|
-
return true;
|
|
299
|
-
}
|
|
300
|
-
return false;
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
// ============================================================================
|
|
304
|
-
// OpLog Operations
|
|
305
|
-
// ============================================================================
|
|
306
|
-
|
|
307
|
-
async appendOpLog(entry: IOpLogEntry): Promise<void> {
|
|
308
|
-
this.opLog.push(entry);
|
|
309
|
-
// Trim oplog if it gets too large (keep last 10000 entries)
|
|
310
|
-
if (this.opLog.length > 10000) {
|
|
311
|
-
this.opLog = this.opLog.slice(-10000);
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
async getOpLogAfter(ts: plugins.bson.Timestamp, limit: number = 1000): Promise<IOpLogEntry[]> {
|
|
316
|
-
const tsValue = ts.toNumber();
|
|
317
|
-
const entries = this.opLog.filter(e => e.ts.toNumber() > tsValue);
|
|
318
|
-
return entries.slice(0, limit);
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
async getLatestOpLogTimestamp(): Promise<plugins.bson.Timestamp | null> {
|
|
322
|
-
if (this.opLog.length === 0) return null;
|
|
323
|
-
return this.opLog[this.opLog.length - 1].ts;
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
/**
|
|
327
|
-
* Generate a new timestamp for oplog entries
|
|
328
|
-
*/
|
|
329
|
-
generateTimestamp(): plugins.bson.Timestamp {
|
|
330
|
-
this.opLogCounter++;
|
|
331
|
-
return new plugins.bson.Timestamp({ t: Math.floor(Date.now() / 1000), i: this.opLogCounter });
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
// ============================================================================
|
|
335
|
-
// Transaction Support
|
|
336
|
-
// ============================================================================
|
|
337
|
-
|
|
338
|
-
async createSnapshot(dbName: string, collName: string): Promise<IStoredDocument[]> {
|
|
339
|
-
const docs = await this.findAll(dbName, collName);
|
|
340
|
-
// Deep clone the documents for snapshot isolation
|
|
341
|
-
return docs.map(doc => JSON.parse(JSON.stringify(doc)));
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
async hasConflicts(
|
|
345
|
-
dbName: string,
|
|
346
|
-
collName: string,
|
|
347
|
-
ids: plugins.bson.ObjectId[],
|
|
348
|
-
snapshotTime: plugins.bson.Timestamp
|
|
349
|
-
): Promise<boolean> {
|
|
350
|
-
// Check if any of the given document IDs have been modified after snapshotTime
|
|
351
|
-
const ns = `${dbName}.${collName}`;
|
|
352
|
-
const modifiedIds = new Set<string>();
|
|
353
|
-
|
|
354
|
-
for (const entry of this.opLog) {
|
|
355
|
-
if (entry.ts.greaterThan(snapshotTime) && entry.ns === ns) {
|
|
356
|
-
if (entry.o._id) {
|
|
357
|
-
modifiedIds.add(entry.o._id.toString());
|
|
358
|
-
}
|
|
359
|
-
if (entry.o2?._id) {
|
|
360
|
-
modifiedIds.add(entry.o2._id.toString());
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
for (const id of ids) {
|
|
366
|
-
if (modifiedIds.has(id.toString())) {
|
|
367
|
-
return true;
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
return false;
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
// ============================================================================
|
|
375
|
-
// Persistence
|
|
376
|
-
// ============================================================================
|
|
377
|
-
|
|
378
|
-
async persist(): Promise<void> {
|
|
379
|
-
if (!this.persistPath) return;
|
|
380
|
-
|
|
381
|
-
const data = {
|
|
382
|
-
databases: {} as Record<string, Record<string, IStoredDocument[]>>,
|
|
383
|
-
indexes: {} as Record<string, Record<string, any[]>>,
|
|
384
|
-
opLogCounter: this.opLogCounter,
|
|
385
|
-
};
|
|
386
|
-
|
|
387
|
-
for (const [dbName, collections] of this.databases) {
|
|
388
|
-
data.databases[dbName] = {};
|
|
389
|
-
for (const [collName, docs] of collections) {
|
|
390
|
-
data.databases[dbName][collName] = Array.from(docs.values());
|
|
391
|
-
}
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
for (const [dbName, collIndexes] of this.indexes) {
|
|
395
|
-
data.indexes[dbName] = {};
|
|
396
|
-
for (const [collName, indexes] of collIndexes) {
|
|
397
|
-
data.indexes[dbName][collName] = indexes;
|
|
398
|
-
}
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
// Ensure parent directory exists
|
|
402
|
-
const dir = this.persistPath.substring(0, this.persistPath.lastIndexOf('/'));
|
|
403
|
-
if (dir) {
|
|
404
|
-
await this.fs.directory(dir).recursive().create();
|
|
405
|
-
}
|
|
406
|
-
await this.fs.file(this.persistPath).encoding('utf8').write(JSON.stringify(data, null, 2));
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
async restore(): Promise<void> {
|
|
410
|
-
if (!this.persistPath) return;
|
|
411
|
-
|
|
412
|
-
try {
|
|
413
|
-
const exists = await this.fs.file(this.persistPath).exists();
|
|
414
|
-
if (!exists) return;
|
|
415
|
-
|
|
416
|
-
const content = await this.fs.file(this.persistPath).encoding('utf8').read();
|
|
417
|
-
const data = JSON.parse(content as string);
|
|
418
|
-
|
|
419
|
-
this.databases.clear();
|
|
420
|
-
this.indexes.clear();
|
|
421
|
-
|
|
422
|
-
for (const [dbName, collections] of Object.entries(data.databases || {})) {
|
|
423
|
-
const dbMap = new Map<string, Map<string, IStoredDocument>>();
|
|
424
|
-
this.databases.set(dbName, dbMap);
|
|
425
|
-
|
|
426
|
-
for (const [collName, docs] of Object.entries(collections as Record<string, any[]>)) {
|
|
427
|
-
const collMap = new Map<string, IStoredDocument>();
|
|
428
|
-
for (const doc of docs) {
|
|
429
|
-
// Restore ObjectId
|
|
430
|
-
if (doc._id && typeof doc._id === 'string') {
|
|
431
|
-
doc._id = new plugins.bson.ObjectId(doc._id);
|
|
432
|
-
} else if (doc._id && typeof doc._id === 'object' && doc._id.$oid) {
|
|
433
|
-
doc._id = new plugins.bson.ObjectId(doc._id.$oid);
|
|
434
|
-
}
|
|
435
|
-
collMap.set(doc._id.toHexString(), doc);
|
|
436
|
-
}
|
|
437
|
-
dbMap.set(collName, collMap);
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
for (const [dbName, collIndexes] of Object.entries(data.indexes || {})) {
|
|
442
|
-
const indexMap = new Map<string, any[]>();
|
|
443
|
-
this.indexes.set(dbName, indexMap);
|
|
444
|
-
for (const [collName, indexes] of Object.entries(collIndexes as Record<string, any[]>)) {
|
|
445
|
-
indexMap.set(collName, indexes);
|
|
446
|
-
}
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
this.opLogCounter = data.opLogCounter || 0;
|
|
450
|
-
} catch (error) {
|
|
451
|
-
// If restore fails, start fresh
|
|
452
|
-
console.warn('Failed to restore from persistence:', error);
|
|
453
|
-
}
|
|
454
|
-
}
|
|
455
|
-
}
|