ruggy 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,34 @@
1
+ const koffi = require('koffi');
2
+ const path = require('path');
3
+
4
+ // Load DLL
5
+ const dllPath = path.join(__dirname, '..', 'lib', 'ruggy.dll');
6
+ const lib = koffi.load(dllPath);
7
+
8
+ // Database operations
9
+ const ruggy_open = lib.func('ruggy_open', 'void *', ['string']);
10
+ const ruggy_db_free = lib.func('ruggy_db_free', 'void', ['void *']);
11
+
12
+ // Collection operations
13
+ const ruggy_get_collection = lib.func('ruggy_get_collection', 'void *', ['void *', 'string']);
14
+ const ruggy_col_free = lib.func('ruggy_col_free', 'void', ['void *']);
15
+
16
+ // Data operations
17
+ const ruggy_insert = lib.func('ruggy_insert', 'void *', ['void *', 'string']);
18
+ const ruggy_find_all = lib.func('ruggy_find_all', 'void *', ['void *']);
19
+ const ruggy_find = lib.func('ruggy_find', 'void *', ['void *', 'string', 'string']);
20
+
21
+ // Memory management
22
+ const ruggy_str_free = lib.func('ruggy_str_free', 'void', ['void *']);
23
+
24
+ module.exports = {
25
+ koffi,
26
+ ruggy_open,
27
+ ruggy_db_free,
28
+ ruggy_get_collection,
29
+ ruggy_col_free,
30
+ ruggy_insert,
31
+ ruggy_find_all,
32
+ ruggy_find,
33
+ ruggy_str_free
34
+ };
package/src/config.js ADDED
@@ -0,0 +1,124 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const yaml = require('js-yaml');
4
+
5
+ /**
6
+ * Cached configuration to avoid repeated file system lookups
7
+ * @type {Object|null}
8
+ */
9
+ let cachedConfig = null;
10
+
11
+ /**
12
+ * Default configuration values
13
+ */
14
+ const DEFAULT_CONFIG = {
15
+ dataPath: './data'
16
+ };
17
+
18
+ /**
19
+ * Searches for ruggy.yaml starting from current directory up to root
20
+ * @param {string} startDir - Directory to start searching from
21
+ * @returns {string|null} - Path to ruggy.yaml or null if not found
22
+ */
23
+ function findConfigFile(startDir = process.cwd()) {
24
+ let currentDir = path.resolve(startDir);
25
+ const root = path.parse(currentDir).root;
26
+
27
+ while (true) {
28
+ const configPath = path.join(currentDir, 'ruggy.yaml');
29
+
30
+ if (fs.existsSync(configPath)) {
31
+ return configPath;
32
+ }
33
+
34
+ // Check for .yml variant
35
+ const altConfigPath = path.join(currentDir, 'ruggy.yml');
36
+ if (fs.existsSync(altConfigPath)) {
37
+ return altConfigPath;
38
+ }
39
+
40
+ // Reached root without finding config
41
+ if (currentDir === root) {
42
+ return null;
43
+ }
44
+
45
+ // Move up one directory
46
+ currentDir = path.dirname(currentDir);
47
+ }
48
+ }
49
+
50
+ /**
51
+ * Loads and parses ruggy.yaml configuration file
52
+ * @param {string} configPath - Path to configuration file
53
+ * @returns {Object} - Parsed configuration
54
+ * @throws {Error} If file cannot be read or parsed
55
+ */
56
+ function parseConfigFile(configPath) {
57
+ try {
58
+ const fileContents = fs.readFileSync(configPath, 'utf8');
59
+ const config = yaml.load(fileContents);
60
+
61
+ if (!config || typeof config !== 'object') {
62
+ throw new Error('Configuration must be a valid YAML object');
63
+ }
64
+
65
+ return config;
66
+ } catch (error) {
67
+ throw new Error(`Failed to parse configuration file at '${configPath}': ${error.message}`);
68
+ }
69
+ }
70
+
71
+ /**
72
+ * Loads Ruggy configuration from ruggy.yaml or returns defaults
73
+ * Configuration is cached after first load
74
+ * @param {Object} options - Options
75
+ * @param {boolean} options.reload - Force reload configuration (bypass cache)
76
+ * @param {string} options.searchFrom - Directory to start searching from (default: process.cwd())
77
+ * @returns {Object} - Configuration object
78
+ */
79
+ function loadConfig(options = {}) {
80
+ const { reload = false, searchFrom = process.cwd() } = options;
81
+
82
+ // Return cached config if available and not forcing reload
83
+ if (cachedConfig && !reload) {
84
+ return cachedConfig;
85
+ }
86
+
87
+ const configPath = findConfigFile(searchFrom);
88
+
89
+ if (!configPath) {
90
+ // No config file found, use defaults
91
+ cachedConfig = { ...DEFAULT_CONFIG };
92
+ return cachedConfig;
93
+ }
94
+
95
+ const userConfig = parseConfigFile(configPath);
96
+
97
+ // Merge with defaults
98
+ cachedConfig = {
99
+ ...DEFAULT_CONFIG,
100
+ ...userConfig
101
+ };
102
+
103
+ // Resolve relative paths to absolute based on config file location
104
+ if (cachedConfig.dataPath && !path.isAbsolute(cachedConfig.dataPath)) {
105
+ const configDir = path.dirname(configPath);
106
+ cachedConfig.dataPath = path.resolve(configDir, cachedConfig.dataPath);
107
+ }
108
+
109
+ return cachedConfig;
110
+ }
111
+
112
+ /**
113
+ * Clears the cached configuration
114
+ * Useful for testing or when configuration changes at runtime
115
+ */
116
+ function clearCache() {
117
+ cachedConfig = null;
118
+ }
119
+
120
+ module.exports = {
121
+ loadConfig,
122
+ clearCache,
123
+ DEFAULT_CONFIG
124
+ };
package/src/index.js ADDED
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Ruggy - A simple, fast embedded database for Node.js
3
+ *
4
+ * @module ruggy
5
+ * @example
6
+ * // Quick usage with auto-cleanup
7
+ * const { RuggyDatabase } = require('ruggy');
8
+ *
9
+ * await RuggyDatabase.withDatabase('./data', async (db) => {
10
+ * await db.withCollection('users', col => {
11
+ * col.insert({ name: 'Alice', age: 30 });
12
+ * console.log(col.findAll());
13
+ * });
14
+ * });
15
+ *
16
+ * @example
17
+ * // Manual connection management
18
+ * const { RuggyDatabase } = require('ruggy');
19
+ *
20
+ * const db = new RuggyDatabase('./data');
21
+ * const users = db.collection('users');
22
+ * users.insert({ name: 'Bob' });
23
+ * users.close();
24
+ * db.close();
25
+ *
26
+ * @example
27
+ * // Connection pooling for servers
28
+ * const { RuggyPool } = require('ruggy');
29
+ *
30
+ * const pool = new RuggyPool('./data');
31
+ *
32
+ * app.get('/users', async (req, res) => {
33
+ * await pool.withCollection('users', col => {
34
+ * res.json(col.findAll());
35
+ * });
36
+ * });
37
+ *
38
+ * process.on('SIGTERM', () => pool.close());
39
+ */
40
+
41
+ const RuggyDatabase = require('./Database');
42
+ // const RuggyCollection = require('./Collection');
43
+ const RuggyPool = require('./Pool');
44
+
45
+ // Export classes
46
+ module.exports = {
47
+ RuggyDatabase,
48
+ // RuggyCollection,
49
+ RuggyPool,
50
+
51
+ // Aliases for convenience
52
+ Ruggy: RuggyDatabase,
53
+ Database: RuggyDatabase,
54
+ // Collection: RuggyCollection,
55
+ Pool: RuggyPool
56
+ };
57
+
58
+ // module.exports.default = RuggyDatabase;
package/src/utils.js ADDED
@@ -0,0 +1,73 @@
1
+ const { koffi } = require('./bindings');
2
+
3
+ /**
4
+ * Reads a null-terminated C string from a memory pointer
5
+ * Optimized to read in chunks instead of byte-by-byte
6
+ * @param {*} ptr - Memory pointer from Koffi
7
+ * @returns {string|null} - UTF-8 decoded string or null
8
+ */
9
+ function readCString(ptr) {
10
+ if (!ptr || koffi.address(ptr) === 0n) return null;
11
+
12
+ const CHUNK_SIZE = 256;
13
+ const MAX_SIZE = 1024 * 1024; // 1MB safety limit
14
+ const chunks = [];
15
+ let offset = 0;
16
+
17
+ while (offset < MAX_SIZE) {
18
+ const buffer = koffi.decode(ptr, offset, koffi.types.uint8, CHUNK_SIZE);
19
+
20
+ // Find null terminator
21
+ let nullIndex = -1;
22
+ for (let i = 0; i < buffer.length; i++) {
23
+ if (buffer[i] === 0) {
24
+ nullIndex = i;
25
+ break;
26
+ }
27
+ }
28
+
29
+ if (nullIndex !== -1) {
30
+ // Found null terminator
31
+ chunks.push(buffer.slice(0, nullIndex));
32
+ break;
33
+ }
34
+
35
+ chunks.push(buffer);
36
+ offset += CHUNK_SIZE;
37
+ }
38
+
39
+ // Concatenate all chunks and decode UTF-8
40
+ const totalLength = chunks.reduce((sum, chunk) => sum + chunk.length, 0);
41
+ const fullBuffer = new Uint8Array(totalLength);
42
+ let pos = 0;
43
+ for (const chunk of chunks) {
44
+ fullBuffer.set(chunk, pos);
45
+ pos += chunk.length;
46
+ }
47
+
48
+ return Buffer.from(fullBuffer).toString('utf8');
49
+ }
50
+
51
+ /**
52
+ * Converts a JavaScript string to a null-terminated UTF-8 buffer
53
+ * @param {string} str - String to convert
54
+ * @returns {Buffer} - Buffer with null terminator
55
+ */
56
+ function toCString(str) {
57
+ return Buffer.from(str + '\0', 'utf8');
58
+ }
59
+
60
+ /**
61
+ * Validates if a pointer is valid (not null)
62
+ * @param {*} ptr - Pointer to validate
63
+ * @returns {boolean}
64
+ */
65
+ function isValidPointer(ptr) {
66
+ return ptr && koffi.address(ptr) !== 0n;
67
+ }
68
+
69
+ module.exports = {
70
+ readCString,
71
+ toCString,
72
+ isValidPointer
73
+ };