sehawq.db 4.0.3 → 4.0.5

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.
@@ -1,295 +1,295 @@
1
- /**
2
- * SehawqDB - The main database class
3
- *
4
- * Started as a simple JSON store, now with performance optimizations
5
- * Because waiting for databases to load is boring 😴
6
- */
7
-
8
- const EventEmitter = require('events');
9
- const path = require('path');
10
- const fs = require('fs').promises;
11
-
12
- class SehawqDB extends EventEmitter {
13
- constructor(options = {}) {
14
- super();
15
-
16
- // Config with some sensible defaults
17
- this.config = {
18
- path: './sehawq-data.json',
19
- autoSave: true,
20
- saveInterval: 5000,
21
- cacheEnabled: true,
22
- cacheSize: 1000,
23
- ...options
24
- };
25
-
26
- // Performance optimizations
27
- this.data = new Map(); // Using Map for better performance
28
- this.cache = new Map(); // Hot data cache
29
- this.indexes = new Map(); // Query indexes
30
-
31
- // Runtime stats (for debugging)
32
- this.stats = {
33
- reads: 0,
34
- writes: 0,
35
- cacheHits: 0,
36
- cacheMisses: 0
37
- };
38
-
39
- this._initialized = false;
40
- this._saveTimeout = null;
41
-
42
- // Initialize - but don't block the constructor
43
- this._init().catch(error => {
44
- console.error('🚨 SehawqDB init failed:', error);
45
- });
46
- }
47
-
48
- /**
49
- * Async initialization
50
- * Because sometimes we need to wait for things...
51
- */
52
- async _init() {
53
- if (this._initialized) return;
54
-
55
- try {
56
- // Try to load existing data
57
- await this._loadFromDisk();
58
-
59
- // Start auto-save if enabled
60
- if (this.config.autoSave) {
61
- this._startAutoSave();
62
- }
63
-
64
- this._initialized = true;
65
- this.emit('ready');
66
-
67
- if (this.config.debug) {
68
- console.log('✅ SehawqDB ready - Performance mode: ON');
69
- }
70
- } catch (error) {
71
- this.emit('error', error);
72
- throw error;
73
- }
74
- }
75
-
76
- /**
77
- * Set a value - the bread and butter
78
- */
79
- set(key, value) {
80
- if (!this._initialized) {
81
- throw new Error('Database not initialized. Wait for ready event.');
82
- }
83
-
84
- const oldValue = this.data.get(key);
85
- this.data.set(key, value);
86
-
87
- // Cache the hot data
88
- if (this.config.cacheEnabled) {
89
- this._updateCache(key, value);
90
- }
91
-
92
- // Update indexes if any
93
- this._updateIndexes(key, value, oldValue);
94
-
95
- this.stats.writes++;
96
- this.emit('set', { key, value, oldValue });
97
-
98
- // Immediate save for important data, otherwise batch it
99
- if (this.config.autoSave) {
100
- this._queueSave();
101
- }
102
-
103
- return this;
104
- }
105
-
106
- /**
107
- * Get a value - faster than your morning coffee
108
- */
109
- get(key) {
110
- if (!this._initialized) {
111
- throw new Error('Database not initialized.');
112
- }
113
-
114
- this.stats.reads++;
115
-
116
- // Check cache first (hot path)
117
- if (this.config.cacheEnabled && this.cache.has(key)) {
118
- this.stats.cacheHits++;
119
- return this.cache.get(key);
120
- }
121
-
122
- this.stats.cacheMisses++;
123
- const value = this.data.get(key);
124
-
125
- // Cache it for next time
126
- if (this.config.cacheEnabled && value !== undefined) {
127
- this._updateCache(key, value);
128
- }
129
-
130
- return value;
131
- }
132
-
133
- /**
134
- * Delete a key - poof! gone.
135
- */
136
- delete(key) {
137
- if (!this.data.has(key)) return false;
138
-
139
- const oldValue = this.data.get(key);
140
- this.data.delete(key);
141
- this.cache.delete(key);
142
- this._removeFromIndexes(key, oldValue);
143
-
144
- this.emit('delete', { key, oldValue });
145
- this._queueSave();
146
-
147
- return true;
148
- }
149
-
150
- /**
151
- * Check if key exists - no guessing games
152
- */
153
- has(key) {
154
- return this.data.has(key);
155
- }
156
-
157
- /**
158
- * Get all data - use carefully!
159
- */
160
- all() {
161
- return Object.fromEntries(this.data);
162
- }
163
-
164
- /**
165
- * Clear everything - the nuclear option
166
- */
167
- clear() {
168
- const size = this.data.size;
169
- this.data.clear();
170
- this.cache.clear();
171
- this.indexes.clear();
172
-
173
- this.emit('clear', { size });
174
- this._queueSave();
175
-
176
- return this;
177
- }
178
-
179
- /**
180
- * Smart cache management
181
- */
182
- _updateCache(key, value) {
183
- // Simple LRU-like cache eviction
184
- if (this.cache.size >= this.config.cacheSize) {
185
- const firstKey = this.cache.keys().next().value;
186
- this.cache.delete(firstKey);
187
- }
188
-
189
- this.cache.set(key, value);
190
- }
191
-
192
- /**
193
- * Index management for faster queries
194
- */
195
- _updateIndexes(key, newValue, oldValue) {
196
- // TODO: Implement in IndexManager.js
197
- // This will make queries lightning fast ⚡
198
- }
199
-
200
- _removeFromIndexes(key, oldValue) {
201
- // TODO: Remove from indexes
202
- }
203
-
204
- /**
205
- * File operations with error handling
206
- */
207
- async _loadFromDisk() {
208
- try {
209
- const data = await fs.readFile(this.config.path, 'utf8');
210
- const parsed = JSON.parse(data);
211
-
212
- // Convert object to Map for better performance
213
- for (const [key, value] of Object.entries(parsed)) {
214
- this.data.set(key, value);
215
- }
216
-
217
- if (this.config.debug) {
218
- console.log(`📁 Loaded ${this.data.size} records from disk`);
219
- }
220
- } catch (error) {
221
- if (error.code === 'ENOENT') {
222
- // File doesn't exist yet - that's fine
223
- if (this.config.debug) {
224
- console.log('📁 No existing data file - starting fresh');
225
- }
226
- } else {
227
- throw error;
228
- }
229
- }
230
- }
231
-
232
- async _saveToDisk() {
233
- try {
234
- const data = JSON.stringify(Object.fromEntries(this.data), null, 2);
235
-
236
- // Atomic write - prevent corruption
237
- const tempPath = this.config.path + '.tmp';
238
- await fs.writeFile(tempPath, data);
239
- await fs.rename(tempPath, this.config.path);
240
-
241
- if (this.config.debug) {
242
- console.log(`💾 Saved ${this.data.size} records to disk`);
243
- }
244
-
245
- this.emit('save', { recordCount: this.data.size });
246
- } catch (error) {
247
- this.emit('error', error);
248
- }
249
- }
250
-
251
- /**
252
- * Batch save operations for performance
253
- */
254
- _queueSave() {
255
- if (this._saveTimeout) clearTimeout(this._saveTimeout);
256
-
257
- this._saveTimeout = setTimeout(() => {
258
- this._saveToDisk();
259
- }, 100); // Batch saves within 100ms window
260
- }
261
-
262
- _startAutoSave() {
263
- setInterval(() => {
264
- if (this._initialized) {
265
- this._saveToDisk();
266
- }
267
- }, this.config.saveInterval);
268
- }
269
-
270
- /**
271
- * Performance monitoring
272
- */
273
- getStats() {
274
- return {
275
- ...this.stats,
276
- cacheHitRate: this.stats.reads > 0
277
- ? (this.stats.cacheHits / this.stats.reads * 100).toFixed(2) + '%'
278
- : '0%',
279
- totalRecords: this.data.size,
280
- cacheSize: this.cache.size
281
- };
282
- }
283
-
284
- /**
285
- * Clean shutdown - be nice to your data
286
- */
287
- async close() {
288
- if (this._saveTimeout) clearTimeout(this._saveTimeout);
289
- await this._saveToDisk();
290
- this._initialized = false;
291
- this.emit('close');
292
- }
293
- }
294
-
1
+ /**
2
+ * SehawqDB - The main database class
3
+ *
4
+ * Started as a simple JSON store, now with performance optimizations
5
+ * Because waiting for databases to load is boring 😴
6
+ */
7
+
8
+ const EventEmitter = require('events');
9
+ const path = require('path');
10
+ const fs = require('fs').promises;
11
+
12
+ class SehawqDB extends EventEmitter {
13
+ constructor(options = {}) {
14
+ super();
15
+
16
+ // Config with some sensible defaults
17
+ this.config = {
18
+ path: './sehawq-data.json',
19
+ autoSave: true,
20
+ saveInterval: 5000,
21
+ cacheEnabled: true,
22
+ cacheSize: 1000,
23
+ ...options
24
+ };
25
+
26
+ // Performance optimizations
27
+ this.data = new Map(); // Using Map for better performance
28
+ this.cache = new Map(); // Hot data cache
29
+ this.indexes = new Map(); // Query indexes
30
+
31
+ // Runtime stats (for debugging)
32
+ this.stats = {
33
+ reads: 0,
34
+ writes: 0,
35
+ cacheHits: 0,
36
+ cacheMisses: 0
37
+ };
38
+
39
+ this._initialized = false;
40
+ this._saveTimeout = null;
41
+
42
+ // Initialize - but don't block the constructor
43
+ this._init().catch(error => {
44
+ console.error('🚨 SehawqDB init failed:', error);
45
+ });
46
+ }
47
+
48
+ /**
49
+ * Async initialization
50
+ * Because sometimes we need to wait for things...
51
+ */
52
+ async _init() {
53
+ if (this._initialized) return;
54
+
55
+ try {
56
+ // Try to load existing data
57
+ await this._loadFromDisk();
58
+
59
+ // Start auto-save if enabled
60
+ if (this.config.autoSave) {
61
+ this._startAutoSave();
62
+ }
63
+
64
+ this._initialized = true;
65
+ this.emit('ready');
66
+
67
+ if (this.config.debug) {
68
+ console.log('✅ SehawqDB ready - Performance mode: ON');
69
+ }
70
+ } catch (error) {
71
+ this.emit('error', error);
72
+ throw error;
73
+ }
74
+ }
75
+
76
+ /**
77
+ * Set a value - the bread and butter
78
+ */
79
+ set(key, value) {
80
+ if (!this._initialized) {
81
+ throw new Error('Database not initialized. Wait for ready event.');
82
+ }
83
+
84
+ const oldValue = this.data.get(key);
85
+ this.data.set(key, value);
86
+
87
+ // Cache the hot data
88
+ if (this.config.cacheEnabled) {
89
+ this._updateCache(key, value);
90
+ }
91
+
92
+ // Update indexes if any
93
+ this._updateIndexes(key, value, oldValue);
94
+
95
+ this.stats.writes++;
96
+ this.emit('set', { key, value, oldValue });
97
+
98
+ // Immediate save for important data, otherwise batch it
99
+ if (this.config.autoSave) {
100
+ this._queueSave();
101
+ }
102
+
103
+ return this;
104
+ }
105
+
106
+ /**
107
+ * Get a value - faster than your morning coffee
108
+ */
109
+ get(key) {
110
+ if (!this._initialized) {
111
+ throw new Error('Database not initialized.');
112
+ }
113
+
114
+ this.stats.reads++;
115
+
116
+ // Check cache first (hot path)
117
+ if (this.config.cacheEnabled && this.cache.has(key)) {
118
+ this.stats.cacheHits++;
119
+ return this.cache.get(key);
120
+ }
121
+
122
+ this.stats.cacheMisses++;
123
+ const value = this.data.get(key);
124
+
125
+ // Cache it for next time
126
+ if (this.config.cacheEnabled && value !== undefined) {
127
+ this._updateCache(key, value);
128
+ }
129
+
130
+ return value;
131
+ }
132
+
133
+ /**
134
+ * Delete a key - poof! gone.
135
+ */
136
+ delete(key) {
137
+ if (!this.data.has(key)) return false;
138
+
139
+ const oldValue = this.data.get(key);
140
+ this.data.delete(key);
141
+ this.cache.delete(key);
142
+ this._removeFromIndexes(key, oldValue);
143
+
144
+ this.emit('delete', { key, oldValue });
145
+ this._queueSave();
146
+
147
+ return true;
148
+ }
149
+
150
+ /**
151
+ * Check if key exists - no guessing games
152
+ */
153
+ has(key) {
154
+ return this.data.has(key);
155
+ }
156
+
157
+ /**
158
+ * Get all data - use carefully!
159
+ */
160
+ all() {
161
+ return Object.fromEntries(this.data);
162
+ }
163
+
164
+ /**
165
+ * Clear everything - the nuclear option
166
+ */
167
+ clear() {
168
+ const size = this.data.size;
169
+ this.data.clear();
170
+ this.cache.clear();
171
+ this.indexes.clear();
172
+
173
+ this.emit('clear', { size });
174
+ this._queueSave();
175
+
176
+ return this;
177
+ }
178
+
179
+ /**
180
+ * Smart cache management
181
+ */
182
+ _updateCache(key, value) {
183
+ // Simple LRU-like cache eviction
184
+ if (this.cache.size >= this.config.cacheSize) {
185
+ const firstKey = this.cache.keys().next().value;
186
+ this.cache.delete(firstKey);
187
+ }
188
+
189
+ this.cache.set(key, value);
190
+ }
191
+
192
+ /**
193
+ * Index management for faster queries
194
+ */
195
+ _updateIndexes(key, newValue, oldValue) {
196
+ // TODO: Implement in IndexManager.js
197
+ // This will make queries lightning fast ⚡
198
+ }
199
+
200
+ _removeFromIndexes(key, oldValue) {
201
+ // TODO: Remove from indexes
202
+ }
203
+
204
+ /**
205
+ * File operations with error handling
206
+ */
207
+ async _loadFromDisk() {
208
+ try {
209
+ const data = await fs.readFile(this.config.path, 'utf8');
210
+ const parsed = JSON.parse(data);
211
+
212
+ // Convert object to Map for better performance
213
+ for (const [key, value] of Object.entries(parsed)) {
214
+ this.data.set(key, value);
215
+ }
216
+
217
+ if (this.config.debug) {
218
+ console.log(`📁 Loaded ${this.data.size} records from disk`);
219
+ }
220
+ } catch (error) {
221
+ if (error.code === 'ENOENT') {
222
+ // File doesn't exist yet - that's fine
223
+ if (this.config.debug) {
224
+ console.log('📁 No existing data file - starting fresh');
225
+ }
226
+ } else {
227
+ throw error;
228
+ }
229
+ }
230
+ }
231
+
232
+ async _saveToDisk() {
233
+ try {
234
+ const data = JSON.stringify(Object.fromEntries(this.data), null, 2);
235
+
236
+ // Atomic write - prevent corruption
237
+ const tempPath = this.config.path + '.tmp';
238
+ await fs.writeFile(tempPath, data);
239
+ await fs.rename(tempPath, this.config.path);
240
+
241
+ if (this.config.debug) {
242
+ console.log(`💾 Saved ${this.data.size} records to disk`);
243
+ }
244
+
245
+ this.emit('save', { recordCount: this.data.size });
246
+ } catch (error) {
247
+ this.emit('error', error);
248
+ }
249
+ }
250
+
251
+ /**
252
+ * Batch save operations for performance
253
+ */
254
+ _queueSave() {
255
+ if (this._saveTimeout) clearTimeout(this._saveTimeout);
256
+
257
+ this._saveTimeout = setTimeout(() => {
258
+ this._saveToDisk();
259
+ }, 100); // Batch saves within 100ms window
260
+ }
261
+
262
+ _startAutoSave() {
263
+ setInterval(() => {
264
+ if (this._initialized) {
265
+ this._saveToDisk();
266
+ }
267
+ }, this.config.saveInterval);
268
+ }
269
+
270
+ /**
271
+ * Performance monitoring
272
+ */
273
+ getStats() {
274
+ return {
275
+ ...this.stats,
276
+ cacheHitRate: this.stats.reads > 0
277
+ ? (this.stats.cacheHits / this.stats.reads * 100).toFixed(2) + '%'
278
+ : '0%',
279
+ totalRecords: this.data.size,
280
+ cacheSize: this.cache.size
281
+ };
282
+ }
283
+
284
+ /**
285
+ * Clean shutdown - be nice to your data
286
+ */
287
+ async close() {
288
+ if (this._saveTimeout) clearTimeout(this._saveTimeout);
289
+ await this._saveToDisk();
290
+ this._initialized = false;
291
+ this.emit('close');
292
+ }
293
+ }
294
+
295
295
  module.exports = SehawqDB;