sehawq.db 2.4.2 → 4.0.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.
@@ -0,0 +1,248 @@
1
+ /**
2
+ * Dot Notation Parser - For nested object access 🔍
3
+ *
4
+ * Turns user.profile.name into actual value
5
+ * Because brackets are so 2010 😄
6
+ */
7
+
8
+ class DotNotation {
9
+ /**
10
+ * Get value using dot notation
11
+ */
12
+ static get(obj, path, defaultValue = undefined) {
13
+ if (!this.isObject(obj) || typeof path !== 'string') {
14
+ return defaultValue;
15
+ }
16
+
17
+ const keys = path.split('.');
18
+ let current = obj;
19
+
20
+ for (const key of keys) {
21
+ if (current === null || current === undefined) {
22
+ return defaultValue;
23
+ }
24
+
25
+ // Handle array indices
26
+ if (Array.isArray(current) && !isNaN(key)) {
27
+ current = current[parseInt(key)];
28
+ } else if (this.isObject(current)) {
29
+ current = current[key];
30
+ } else {
31
+ return defaultValue;
32
+ }
33
+ }
34
+
35
+ return current !== undefined ? current : defaultValue;
36
+ }
37
+
38
+ /**
39
+ * Set value using dot notation
40
+ */
41
+ static set(obj, path, value) {
42
+ if (!this.isObject(obj) || typeof path !== 'string') {
43
+ return false;
44
+ }
45
+
46
+ const keys = path.split('.');
47
+ let current = obj;
48
+
49
+ for (let i = 0; i < keys.length - 1; i++) {
50
+ const key = keys[i];
51
+ const nextKey = keys[i + 1];
52
+
53
+ // Create nested objects if they don't exist
54
+ if (current[key] === undefined || current[key] === null) {
55
+ // Check if next key is numeric (array)
56
+ current[key] = !isNaN(nextKey) ? [] : {};
57
+ }
58
+
59
+ current = current[key];
60
+ }
61
+
62
+ const lastKey = keys[keys.length - 1];
63
+
64
+ // Handle array indices
65
+ if (Array.isArray(current) && !isNaN(lastKey)) {
66
+ const index = parseInt(lastKey);
67
+ current[index] = value;
68
+ } else {
69
+ current[lastKey] = value;
70
+ }
71
+
72
+ return true;
73
+ }
74
+
75
+ /**
76
+ * Check if path exists
77
+ */
78
+ static has(obj, path) {
79
+ if (!this.isObject(obj) || typeof path !== 'string') {
80
+ return false;
81
+ }
82
+
83
+ const keys = path.split('.');
84
+ let current = obj;
85
+
86
+ for (const key of keys) {
87
+ if (current === null || current === undefined) {
88
+ return false;
89
+ }
90
+
91
+ if (Array.isArray(current) && !isNaN(key)) {
92
+ const index = parseInt(key);
93
+ if (index < 0 || index >= current.length) {
94
+ return false;
95
+ }
96
+ current = current[index];
97
+ } else if (this.isObject(current)) {
98
+ if (!current.hasOwnProperty(key)) {
99
+ return false;
100
+ }
101
+ current = current[key];
102
+ } else {
103
+ return false;
104
+ }
105
+ }
106
+
107
+ return true;
108
+ }
109
+
110
+ /**
111
+ * Delete value using dot notation
112
+ */
113
+ static delete(obj, path) {
114
+ if (!this.isObject(obj) || typeof path !== 'string') {
115
+ return false;
116
+ }
117
+
118
+ const keys = path.split('.');
119
+ let current = obj;
120
+
121
+ for (let i = 0; i < keys.length - 1; i++) {
122
+ const key = keys[i];
123
+
124
+ if (current[key] === undefined || current[key] === null) {
125
+ return false;
126
+ }
127
+
128
+ current = current[key];
129
+ }
130
+
131
+ const lastKey = keys[keys.length - 1];
132
+
133
+ if (Array.isArray(current) && !isNaN(lastKey)) {
134
+ const index = parseInt(lastKey);
135
+ if (index >= 0 && index < current.length) {
136
+ current.splice(index, 1);
137
+ return true;
138
+ }
139
+ } else if (this.isObject(current)) {
140
+ if (current.hasOwnProperty(lastKey)) {
141
+ delete current[lastKey];
142
+ return true;
143
+ }
144
+ }
145
+
146
+ return false;
147
+ }
148
+
149
+ /**
150
+ * Get all paths in an object
151
+ */
152
+ static getAllPaths(obj, prefix = '') {
153
+ if (!this.isObject(obj)) return [];
154
+
155
+ const paths = [];
156
+
157
+ for (const key in obj) {
158
+ if (obj.hasOwnProperty(key)) {
159
+ const currentPath = prefix ? `${prefix}.${key}` : key;
160
+ const value = obj[key];
161
+
162
+ if (this.isObject(value) && !Array.isArray(value)) {
163
+ // Recursively get paths for nested objects
164
+ paths.push(...this.getAllPaths(value, currentPath));
165
+ } else if (Array.isArray(value)) {
166
+ // Handle arrays - include index paths
167
+ paths.push(currentPath);
168
+ for (let i = 0; i < value.length; i++) {
169
+ const arrayPath = `${currentPath}.${i}`;
170
+ if (this.isObject(value[i])) {
171
+ paths.push(...this.getAllPaths(value[i], arrayPath));
172
+ } else {
173
+ paths.push(arrayPath);
174
+ }
175
+ }
176
+ } else {
177
+ paths.push(currentPath);
178
+ }
179
+ }
180
+ }
181
+
182
+ return paths;
183
+ }
184
+
185
+ /**
186
+ * Flatten object using dot notation
187
+ */
188
+ static flatten(obj) {
189
+ const result = {};
190
+
191
+ function flattenHelper(current, path) {
192
+ if (DotNotation.isObject(current) && !Array.isArray(current)) {
193
+ for (const key in current) {
194
+ if (current.hasOwnProperty(key)) {
195
+ const newPath = path ? `${path}.${key}` : key;
196
+ flattenHelper(current[key], newPath);
197
+ }
198
+ }
199
+ } else if (Array.isArray(current)) {
200
+ for (let i = 0; i < current.length; i++) {
201
+ const newPath = `${path}.${i}`;
202
+ flattenHelper(current[i], newPath);
203
+ }
204
+ } else {
205
+ result[path] = current;
206
+ }
207
+ }
208
+
209
+ flattenHelper(obj, '');
210
+ return result;
211
+ }
212
+
213
+ /**
214
+ * Unflatten object (reverse of flatten)
215
+ */
216
+ static unflatten(flatObj) {
217
+ const result = {};
218
+
219
+ for (const path in flatObj) {
220
+ if (flatObj.hasOwnProperty(path)) {
221
+ this.set(result, path, flatObj[path]);
222
+ }
223
+ }
224
+
225
+ return result;
226
+ }
227
+
228
+ /**
229
+ * Check if value is an object
230
+ */
231
+ static isObject(value) {
232
+ return value !== null && typeof value === 'object';
233
+ }
234
+
235
+ /**
236
+ * Parse path into keys array
237
+ */
238
+ static parsePath(path) {
239
+ if (typeof path !== 'string') return [];
240
+
241
+ // Handle array indices and nested objects
242
+ return path.split('.').map(key => {
243
+ return !isNaN(key) ? parseInt(key) : key;
244
+ });
245
+ }
246
+ }
247
+
248
+ module.exports = DotNotation;
@@ -0,0 +1,276 @@
1
+ /**
2
+ * Helper Functions - The Swiss Army knife of utilities 🔧
3
+ *
4
+ * Random useful stuff that makes life easier
5
+ * Collected from years of "oh, I need that again" moments
6
+ */
7
+
8
+ class Helpers {
9
+ /**
10
+ * Deep clone an object
11
+ */
12
+ static deepClone(obj) {
13
+ if (obj === null || typeof obj !== 'object') return obj;
14
+ if (obj instanceof Date) return new Date(obj.getTime());
15
+ if (obj instanceof Array) return obj.map(item => Helpers.deepClone(item));
16
+
17
+ const cloned = {};
18
+ for (const key in obj) {
19
+ if (obj.hasOwnProperty(key)) {
20
+ cloned[key] = Helpers.deepClone(obj[key]);
21
+ }
22
+ }
23
+ return cloned;
24
+ }
25
+
26
+ /**
27
+ * Deep merge objects
28
+ */
29
+ static deepMerge(target, ...sources) {
30
+ if (!sources.length) return target;
31
+ const source = sources.shift();
32
+
33
+ if (Helpers.isObject(target) && Helpers.isObject(source)) {
34
+ for (const key in source) {
35
+ if (Helpers.isObject(source[key])) {
36
+ if (!target[key]) Object.assign(target, { [key]: {} });
37
+ Helpers.deepMerge(target[key], source[key]);
38
+ } else {
39
+ Object.assign(target, { [key]: source[key] });
40
+ }
41
+ }
42
+ }
43
+
44
+ return Helpers.deepMerge(target, ...sources);
45
+ }
46
+
47
+ /**
48
+ * Check if value is an object
49
+ */
50
+ static isObject(item) {
51
+ return item && typeof item === 'object' && !Array.isArray(item);
52
+ }
53
+
54
+ /**
55
+ * Generate random ID
56
+ */
57
+ static generateId(length = 16) {
58
+ const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
59
+ let result = '';
60
+ for (let i = 0; i < length; i++) {
61
+ result += chars.charAt(Math.floor(Math.random() * chars.length));
62
+ }
63
+ return result;
64
+ }
65
+
66
+ /**
67
+ * Sleep/delay function
68
+ */
69
+ static sleep(ms) {
70
+ return new Promise(resolve => setTimeout(resolve, ms));
71
+ }
72
+
73
+ /**
74
+ * Debounce function
75
+ */
76
+ static debounce(func, wait, immediate = false) {
77
+ let timeout;
78
+ return function executedFunction(...args) {
79
+ const later = () => {
80
+ timeout = null;
81
+ if (!immediate) func(...args);
82
+ };
83
+ const callNow = immediate && !timeout;
84
+ clearTimeout(timeout);
85
+ timeout = setTimeout(later, wait);
86
+ if (callNow) func(...args);
87
+ };
88
+ }
89
+
90
+ /**
91
+ * Throttle function
92
+ */
93
+ static throttle(func, limit) {
94
+ let inThrottle;
95
+ return function(...args) {
96
+ if (!inThrottle) {
97
+ func.apply(this, args);
98
+ inThrottle = true;
99
+ setTimeout(() => inThrottle = false, limit);
100
+ }
101
+ };
102
+ }
103
+
104
+ /**
105
+ * Check if value is empty
106
+ */
107
+ static isEmpty(value) {
108
+ if (value === null || value === undefined) return true;
109
+ if (typeof value === 'string') return value.trim().length === 0;
110
+ if (Array.isArray(value)) return value.length === 0;
111
+ if (Helpers.isObject(value)) return Object.keys(value).length === 0;
112
+ return false;
113
+ }
114
+
115
+ /**
116
+ * Get object size (bytes estimation)
117
+ */
118
+ static getObjectSize(obj) {
119
+ const seen = new WeakSet();
120
+
121
+ function sizeOf(obj) {
122
+ if (obj === null || obj === undefined) return 0;
123
+ if (seen.has(obj)) return 0;
124
+
125
+ seen.add(obj);
126
+
127
+ switch (typeof obj) {
128
+ case 'number':
129
+ return 8;
130
+ case 'string':
131
+ return obj.length * 2;
132
+ case 'boolean':
133
+ return 4;
134
+ case 'object':
135
+ if (Array.isArray(obj)) {
136
+ return obj.reduce((size, item) => size + sizeOf(item), 0);
137
+ } else if (obj instanceof Map) {
138
+ let size = 0;
139
+ for (const [key, value] of obj) {
140
+ size += sizeOf(key) + sizeOf(value);
141
+ }
142
+ return size;
143
+ } else if (obj instanceof Set) {
144
+ let size = 0;
145
+ for (const value of obj) {
146
+ size += sizeOf(value);
147
+ }
148
+ return size;
149
+ } else {
150
+ let size = 0;
151
+ for (const key in obj) {
152
+ if (obj.hasOwnProperty(key)) {
153
+ size += sizeOf(key) + sizeOf(obj[key]);
154
+ }
155
+ }
156
+ return size;
157
+ }
158
+ default:
159
+ return 0;
160
+ }
161
+ }
162
+
163
+ return sizeOf(obj);
164
+ }
165
+
166
+ /**
167
+ * Format bytes to human readable
168
+ */
169
+ static formatBytes(bytes, decimals = 2) {
170
+ if (bytes === 0) return '0 Bytes';
171
+
172
+ const k = 1024;
173
+ const dm = decimals < 0 ? 0 : decimals;
174
+ const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
175
+
176
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
177
+
178
+ return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
179
+ }
180
+
181
+ /**
182
+ * Safe JSON parse
183
+ */
184
+ static safeJsonParse(str, defaultValue = null) {
185
+ try {
186
+ return JSON.parse(str);
187
+ } catch (error) {
188
+ return defaultValue;
189
+ }
190
+ }
191
+
192
+ /**
193
+ * Safe JSON stringify
194
+ */
195
+ static safeJsonStringify(obj, defaultValue = '{}') {
196
+ try {
197
+ return JSON.stringify(obj);
198
+ } catch (error) {
199
+ return defaultValue;
200
+ }
201
+ }
202
+
203
+ /**
204
+ * Filter object properties
205
+ */
206
+ static filterObject(obj, predicate) {
207
+ const result = {};
208
+ for (const [key, value] of Object.entries(obj)) {
209
+ if (predicate(value, key)) {
210
+ result[key] = value;
211
+ }
212
+ }
213
+ return result;
214
+ }
215
+
216
+ /**
217
+ * Map object properties
218
+ */
219
+ static mapObject(obj, mapper) {
220
+ const result = {};
221
+ for (const [key, value] of Object.entries(obj)) {
222
+ result[key] = mapper(value, key);
223
+ }
224
+ return result;
225
+ }
226
+
227
+ /**
228
+ * Retry function with exponential backoff
229
+ */
230
+ static async retry(fn, retries = 3, delay = 1000) {
231
+ try {
232
+ return await fn();
233
+ } catch (error) {
234
+ if (retries === 0) throw error;
235
+ await Helpers.sleep(delay);
236
+ return Helpers.retry(fn, retries - 1, delay * 2);
237
+ }
238
+ }
239
+
240
+ /**
241
+ * Generate timestamp
242
+ */
243
+ static timestamp() {
244
+ return Date.now();
245
+ }
246
+
247
+ /**
248
+ * Generate UUID v4
249
+ */
250
+ static uuid() {
251
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
252
+ const r = Math.random() * 16 | 0;
253
+ const v = c == 'x' ? r : (r & 0x3 | 0x8);
254
+ return v.toString(16);
255
+ });
256
+ }
257
+
258
+ /**
259
+ * Check if running in Node.js
260
+ */
261
+ static isNode() {
262
+ return typeof process !== 'undefined' &&
263
+ process.versions != null &&
264
+ process.versions.node != null;
265
+ }
266
+
267
+ /**
268
+ * Check if running in browser
269
+ */
270
+ static isBrowser() {
271
+ return typeof window !== 'undefined' &&
272
+ typeof window.document !== 'undefined';
273
+ }
274
+ }
275
+
276
+ module.exports = Helpers;
@@ -0,0 +1,71 @@
1
+ const fs = require("fs").promises;
2
+
3
+ class Storage {
4
+ constructor(filePath) {
5
+ this.filePath = filePath;
6
+ this.writeQueue = [];
7
+ this.isWriting = false;
8
+ this.debounceTimer = null;
9
+ }
10
+
11
+ async init() {
12
+ try {
13
+ await fs.access(this.filePath);
14
+ } catch {
15
+ await fs.writeFile(this.filePath, JSON.stringify({}), "utf8");
16
+ }
17
+
18
+ try {
19
+ const content = await fs.readFile(this.filePath, "utf8");
20
+ return JSON.parse(content);
21
+ } catch {
22
+ return {};
23
+ }
24
+ }
25
+
26
+ // Debounced write - waits 100ms before writing
27
+ async write(data, immediate = false) {
28
+ if (immediate) {
29
+ return this._writeNow(data);
30
+ }
31
+
32
+ // Clear existing timer
33
+ if (this.debounceTimer) {
34
+ clearTimeout(this.debounceTimer);
35
+ }
36
+
37
+ // Set new timer
38
+ return new Promise((resolve) => {
39
+ this.debounceTimer = setTimeout(async () => {
40
+ await this._writeNow(data);
41
+ resolve();
42
+ }, 100);
43
+ });
44
+ }
45
+
46
+ async _writeNow(data) {
47
+ const tmpPath = `${this.filePath}.tmp`;
48
+
49
+ try {
50
+ await fs.writeFile(tmpPath, JSON.stringify(data, null, 2), "utf8");
51
+ await fs.rename(tmpPath, this.filePath);
52
+ } catch (err) {
53
+ // Cleanup temp file if it exists
54
+ try {
55
+ await fs.unlink(tmpPath);
56
+ } catch {}
57
+ throw err;
58
+ }
59
+ }
60
+
61
+ async backup(backupPath, data) {
62
+ await fs.writeFile(backupPath, JSON.stringify(data, null, 2), "utf8");
63
+ }
64
+
65
+ async restore(backupPath) {
66
+ const content = await fs.readFile(backupPath, "utf8");
67
+ return JSON.parse(content);
68
+ }
69
+ }
70
+
71
+ module.exports = Storage;
package/src/version.js ADDED
@@ -0,0 +1,38 @@
1
+ /**
2
+ * SehawqDB Version Information
3
+ *
4
+ * Because knowing your version is kinda important 😅
5
+ */
6
+
7
+ const packageJson = require('../package.json');
8
+
9
+ module.exports = {
10
+ version: packageJson.version,
11
+ name: packageJson.name,
12
+ description: packageJson.description,
13
+ author: packageJson.author,
14
+ license: packageJson.license,
15
+
16
+ getVersion() {
17
+ return this.version;
18
+ },
19
+
20
+ getInfo() {
21
+ return {
22
+ name: this.name,
23
+ version: this.version,
24
+ description: this.description,
25
+ author: this.author,
26
+ license: this.license
27
+ };
28
+ },
29
+
30
+ // Compatibility check
31
+ isCompatibleWith(version) {
32
+ const current = this.version.split('.').map(Number);
33
+ const target = version.split('.').map(Number);
34
+
35
+ // Major version must match, minor can be equal or higher
36
+ return current[0] === target[0] && current[1] >= target[1];
37
+ }
38
+ };