crondb-driver 1.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.
Files changed (4) hide show
  1. package/README.md +16 -0
  2. package/crondb.js +159 -0
  3. package/package.json +19 -0
  4. package/test_sdk.js +79 -0
package/README.md ADDED
@@ -0,0 +1,16 @@
1
+ # CronDB Node.js SDK
2
+
3
+ The official asynchronous ORM driver for **CronDB**, a high-performance, temporal C++ database engine.
4
+
5
+ Traditional architectures force you to build "split-brain" systems to handle time-based events: storing the data in a database while pushing the timers to an external task queue.
6
+
7
+ **CronDB unifies state and time.** You inject finite-state-machine (FSM) timers directly into your database rows. When a state naturally expires, the C++ engine's asynchronous threads instantly push a webhook to your Node.js server.
8
+
9
+ No polling. No external queues. Just one unified temporal engine.
10
+
11
+ ---
12
+
13
+ ## 📦 Installation
14
+
15
+ ```bash
16
+ npm install crondb
package/crondb.js ADDED
@@ -0,0 +1,159 @@
1
+ const http = require('http');
2
+
3
+ class CronDB {
4
+ /**
5
+ * Initialize the CronDB Client
6
+ * @param {string} host - The IP of the C++ Engine
7
+ * @param {number} port - The port the engine is listening on
8
+ */
9
+ constructor(host = '127.0.0.1', port = 8080) {
10
+ this.host = host;
11
+ this.port = port;
12
+ // Keep-Alive Agent to prevent TCP exhaustion during heavy FSM loads
13
+ this.agent = new http.Agent({ keepAlive: true, maxSockets: 100 });
14
+ }
15
+
16
+ /**
17
+ * Send a raw SQL command to the engine
18
+ * @param {string} sqlString - The CronDB SQL command
19
+ * @returns {Promise<Object|string>} - The JSON or String response
20
+ */
21
+ async query(sqlString) {
22
+ return new Promise((resolve, reject) => {
23
+ const options = {
24
+ hostname: this.host,
25
+ port: this.port,
26
+ path: '/query',
27
+ method: 'POST',
28
+ agent: this.agent, // Uses our connection pool
29
+ headers: {
30
+ 'Content-Type': 'text/plain',
31
+ 'Content-Length': Buffer.byteLength(sqlString)
32
+ }
33
+ };
34
+
35
+ const req = http.request(options, (res) => {
36
+ let data = '';
37
+ res.on('data', (chunk) => { data += chunk; });
38
+ res.on('end', () => {
39
+ try {
40
+ resolve(JSON.parse(data)); // Try to parse pure JSON
41
+ } catch (e) {
42
+ resolve(data); // Fallback for raw strings like "success!"
43
+ }
44
+ });
45
+ });
46
+
47
+ req.on('error', (e) => reject(`[CronDB Connection Error]: ${e.message}`));
48
+ req.write(sqlString);
49
+ req.end();
50
+ });
51
+ }
52
+
53
+ // ==========================================================
54
+ // ORM LAYER (Object-Relational Mapping)
55
+ // ==========================================================
56
+
57
+ /**
58
+ * Insert a native JavaScript object into the database
59
+ * @param {string} tableName
60
+ * @param {Object} dataObj - e.g., { name: "Lancelot", hp: 100 }
61
+ */
62
+ async insert(tableName, dataObj) {
63
+ let sql = `INSERT INTO ${tableName}`;
64
+
65
+ for (const [key, value] of Object.entries(dataObj)) {
66
+ if (typeof value === 'number' || typeof value === 'boolean') {
67
+ // Numbers and booleans stay raw
68
+ sql += ` ${key}=${value}`;
69
+ } else if (typeof value === 'string') {
70
+ const trimmed = value.trim();
71
+ // Smart check: Is it a CronDB Temporal FSM? e.g. "( 'Alive' --> 'Dead' @ 5 s )"
72
+ if (trimmed.startsWith('(') && trimmed.endsWith(')')) {
73
+ sql += ` ${key}=${trimmed}`; // Don't quote FSMs
74
+ } else {
75
+ sql += ` ${key}='${value}'`; // Wrap normal strings in quotes
76
+ }
77
+ }
78
+ }
79
+ return await this.query(sql);
80
+ }
81
+
82
+ /**
83
+ * Attach a Webhook Listener to a Temporal Column
84
+ * @param {string} tableName - The target table
85
+ * @param {string} columnName - The column with the time-state
86
+ * @param {string} webhookUrl - Where the C++ engine should shoot the HTTP POST
87
+ * @param {string} [whereClause=null] - Optional condition (e.g., "pid = 5")
88
+ */
89
+ async listen(tableName, columnName, webhookUrl, whereClause = null) {
90
+ let sql = `LISTEN ${tableName} . ${columnName} --> '${webhookUrl}'`;
91
+
92
+ // If the developer wants to listen to a specific row, append it!
93
+ if (whereClause) {
94
+ sql += ` WHERE ${whereClause}`;
95
+ }
96
+
97
+ return await this.query(sql);
98
+ }
99
+
100
+ /**
101
+ * READ: Select rows from a table
102
+ * @param {string} tableName
103
+ * @param {string} [whereClause=null] - Optional (e.g., "energy < 50")
104
+ */
105
+ async select(tableName, whereClause = null) {
106
+ let sql = `SELECT * FROM ${tableName}`;
107
+ if (whereClause) {
108
+ sql += ` WHERE ${whereClause}`;
109
+ }
110
+ return await this.query(sql);
111
+ }
112
+
113
+ /**
114
+ * UPDATE: Modify existing rows using a native JS object
115
+ * @param {string} tableName
116
+ * @param {Object} dataObj - The fields to update (e.g., { status: "Dead", hp: 0 })
117
+ * @param {string} whereClause - Required for safety (e.g., "pid = 5")
118
+ */
119
+ async update(tableName, dataObj, whereClause) {
120
+ if (!whereClause) throw new Error("CronDB ORM: Update requires a WHERE clause to prevent accidental table wipes.");
121
+
122
+ let sql = `UPDATE ${tableName} SET`;
123
+ let isFirst = true;
124
+
125
+ for (const [key, value] of Object.entries(dataObj)) {
126
+ if (!isFirst) sql += ","; // Optional: depending on if your C++ parser uses commas for UPDATE
127
+ isFirst = false;
128
+
129
+ if (typeof value === 'number' || typeof value === 'boolean') {
130
+ sql += ` ${key}=${value}`;
131
+ } else if (typeof value === 'string') {
132
+ const trimmed = value.trim();
133
+ // Smart check for Temporal FSMs
134
+ if (trimmed.startsWith('(') && trimmed.endsWith(')')) {
135
+ sql += ` ${key}=${trimmed}`;
136
+ } else {
137
+ sql += ` ${key}='${value}'`;
138
+ }
139
+ }
140
+ }
141
+
142
+ sql += ` WHERE ${whereClause}`;
143
+ return await this.query(sql);
144
+ }
145
+
146
+ /**
147
+ * DELETE: Remove rows from a table
148
+ * @param {string} tableName
149
+ * @param {string} whereClause - Required for safety
150
+ */
151
+ async delete(tableName, whereClause) {
152
+ if (!whereClause) throw new Error("CronDB ORM: Delete requires a WHERE clause.");
153
+
154
+ let sql = `DELETE FROM ${tableName} WHERE ${whereClause}`;
155
+ return await this.query(sql);
156
+ }
157
+ }
158
+
159
+ module.exports = CronDB;
package/package.json ADDED
@@ -0,0 +1,19 @@
1
+ {
2
+ "name": "crondb-driver",
3
+ "version": "1.0.0",
4
+ "description": "The official Node.js ORM and connection driver for the CronDB temporal database engine.",
5
+ "main": "crondb.js",
6
+ "scripts": {
7
+ "test": "node test_sdk.js"
8
+ },
9
+ "keywords": [
10
+ "database",
11
+ "temporal",
12
+ "fsm",
13
+ "orm",
14
+ "crondb",
15
+ "events"
16
+ ],
17
+ "author": "Michael Tal",
18
+ "license": "ISC"
19
+ }
package/test_sdk.js ADDED
@@ -0,0 +1,79 @@
1
+ const CronDB = require('./crondb');
2
+ const http = require('http');
3
+
4
+ // 1. Spin up a tiny temporary web server to catch the temporal webhooks
5
+ const catcher = http.createServer((req, res) => {
6
+ let body = '';
7
+ req.on('data', chunk => body += chunk.toString());
8
+ req.on('end', () => {
9
+ console.log(`\n🎯 [WEBHOOK CAUGHT] Node.js received: ${body}`);
10
+ res.writeHead(200);
11
+ res.end();
12
+ });
13
+ });
14
+ catcher.listen(3000);
15
+
16
+ // 2. Initialize the SDK
17
+ const db = new CronDB('127.0.0.1', 8080);
18
+
19
+ // Helper function to pause our Node.js script so we can watch time pass
20
+ const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
21
+
22
+ async function runIntegrationTest() {
23
+ console.log("=== STARTING CRONDB SDK INTEGRATION TEST ===\n");
24
+
25
+ try {
26
+ console.log("[Phase 1] Forging the schema (Raw Query)...");
27
+ let res = await db.query("CREATE TABLE warriors wid ApexInt name string hp int status string 4");
28
+ console.log("Engine Reply:", res);
29
+
30
+ console.log("\n[Phase 2] Arming the Webhook Listener...");
31
+ res = await db.listen('warriors', 'status', 'http://127.0.0.1:3000');
32
+ console.log("Engine Reply:", res);
33
+
34
+ console.log("\n[Phase 3] CREATE: Using ORM to Insert Data...");
35
+ // Normal JS Object
36
+ await db.insert('warriors', {
37
+ name: 'Tank',
38
+ hp: 200,
39
+ status: 'Defending'
40
+ });
41
+
42
+ // Temporal FSM Object (Fires in 3 seconds!)
43
+ await db.insert('warriors', {
44
+ name: 'Assassin',
45
+ hp: 80,
46
+ status: "('Hidden' --> 'Revealed' @ 3 s)"
47
+ });
48
+ console.log("Engine Reply: Data successfully planted.");
49
+
50
+ console.log("\n[Phase 4] READ: Using ORM to view Data...");
51
+ let rows = await db.select('warriors');
52
+ console.log("Current State:", rows);
53
+
54
+ console.log("\n[Phase 5] UPDATE: Using ORM to modify Data...");
55
+ await db.update('warriors', { hp: 150 }, "name = 'Tank'");
56
+ console.log("Engine Reply: Tank HP updated to 150.");
57
+
58
+ console.log("\n[Phase 6] DELETE: Using ORM to drop Data...");
59
+ await db.delete('warriors', "name = 'Tank'");
60
+ console.log("Engine Reply: Tank deleted.");
61
+
62
+ console.log("\n[Phase 7] Waiting for Temporal Detonation (3 seconds)...");
63
+ console.log("The clock is ticking...");
64
+ await sleep(4000); // Wait 4 seconds to ensure the 3-second C++ timer pops
65
+
66
+ console.log("\n[Phase 8] Final Read to verify state changes...");
67
+ rows = await db.select('warriors');
68
+ console.log("Final State (Assassin should be Revealed):", rows);
69
+
70
+ } catch (err) {
71
+ console.error("\n❌ Test Failed:", err);
72
+ } finally {
73
+ console.log("\n=== TEST COMPLETE. SHUTTING DOWN ===");
74
+ catcher.close();
75
+ process.exit(0);
76
+ }
77
+ }
78
+
79
+ runIntegrationTest();