@wowoengine/sawitdb 2.4.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.
Binary file
package/package.json ADDED
@@ -0,0 +1,63 @@
1
+ {
2
+ "name": "@wowoengine/sawitdb",
3
+ "publishConfig": {
4
+ "access": "public"
5
+ },
6
+ "version": "2.4.0",
7
+ "description": "A networked document database with Indonesian Agricultural Query Language (AQL) - Network Edition",
8
+ "main": "src/WowoEngine.js",
9
+ "scripts": {
10
+ "start": "node bin/sawit-server.js",
11
+ "server": "node bin/sawit-server.js",
12
+ "test": "node tests/test_server_injection.js",
13
+ "example": "node examples/example_client.js",
14
+ "cli": "node cli/remote.js sawitdb://localhost:7878/default",
15
+ "dev": "SAWIT_LOG_LEVEL=debug node bin/sawit-server.js"
16
+ },
17
+ "keywords": [
18
+ "database",
19
+ "database-engine",
20
+ "database-server",
21
+ "database-client",
22
+ "database-remote",
23
+ "database-local",
24
+ "database-embedded",
25
+ "database-single-file",
26
+ "database-binary",
27
+ "database-file",
28
+ "sawitdb",
29
+ "wowoengine",
30
+ "wowoengine/sawitdb",
31
+ "document-database",
32
+ "nosql",
33
+ "json",
34
+ "btree",
35
+ "indexing",
36
+ "tcp-server",
37
+ "networking",
38
+ "agricultural",
39
+ "indonesian",
40
+ "mongodb-like",
41
+ "embedded-database"
42
+ ],
43
+ "author": "SawitDB Community",
44
+ "license": "MIT",
45
+ "repository": {
46
+ "type": "git",
47
+ "url": "https://github.com/WowoEngine/SawitDB.git"
48
+ },
49
+ "engines": {
50
+ "node": ">=12.0.0"
51
+ },
52
+ "bin": {
53
+ "sawitdb-server": "./bin/sawit-server.js",
54
+ "sawitdb-cli": "./cli/remote.js"
55
+ },
56
+ "files": [
57
+ "src/",
58
+ "bin/",
59
+ "cli/",
60
+ "docs/",
61
+ "README.md"
62
+ ]
63
+ }
@@ -0,0 +1,265 @@
1
+ const net = require('net');
2
+ const { URL } = require('url');
3
+
4
+ /**
5
+ * SawitDB Client - Connect to SawitDB Server
6
+ * Usage: sawitdb://[username:password@]host:port/database
7
+ */
8
+ class SawitClient {
9
+ constructor(connectionString) {
10
+ this.connectionString = connectionString;
11
+ this.socket = null;
12
+ this.connected = false;
13
+ this.authenticated = false;
14
+ this.currentDatabase = null;
15
+ this.buffer = '';
16
+ this.pendingRequests = [];
17
+ this.requestId = 0;
18
+
19
+ this._parseConnectionString(connectionString);
20
+ }
21
+
22
+ _parseConnectionString(connStr) {
23
+ // Parse sawitdb://[user:pass@]host:port/database
24
+ const url = connStr.replace('sawitdb://', 'http://'); // Trick to use URL parser
25
+ const parsed = new URL(url);
26
+
27
+ this.host = parsed.hostname || 'localhost';
28
+ this.port = parseInt(parsed.port) || 7878;
29
+ this.database = parsed.pathname.replace('/', '') || null;
30
+ this.username = parsed.username || null;
31
+ this.password = parsed.password || null;
32
+ }
33
+
34
+ async connect() {
35
+ return new Promise((resolve, reject) => {
36
+ this.socket = net.createConnection({ host: this.host, port: this.port }, () => {
37
+ console.log(`[Client] Connected to ${this.host}:${this.port}`);
38
+ this.connected = true;
39
+ });
40
+
41
+ this.socket.on('data', (data) => {
42
+ this._handleData(data);
43
+ });
44
+
45
+ this.socket.on('end', () => {
46
+ console.log('[Client] Disconnected from server');
47
+ this.connected = false;
48
+ });
49
+
50
+ this.socket.on('error', (err) => {
51
+ console.error('[Client] Socket error:', err.message);
52
+ this.connected = false;
53
+ reject(err);
54
+ });
55
+
56
+ // Wait for welcome message
57
+ const welcomeHandler = (response) => {
58
+ if (response.type === 'welcome') {
59
+ console.log(`[Client] ${response.message} v${response.version}`);
60
+ this._initConnection().then(resolve).catch(reject);
61
+ }
62
+ };
63
+
64
+ this.pendingRequests.push({ id: 'welcome', handler: welcomeHandler });
65
+ });
66
+ }
67
+
68
+ async _initConnection() {
69
+ // Authenticate if credentials provided
70
+ if (this.username && this.password) {
71
+ await this._authenticate();
72
+ }
73
+
74
+ // Select database if specified
75
+ if (this.database) {
76
+ await this.use(this.database);
77
+ }
78
+ }
79
+
80
+ async _authenticate() {
81
+ return new Promise((resolve, reject) => {
82
+ this._sendRequest({
83
+ type: 'auth',
84
+ payload: {
85
+ username: this.username,
86
+ password: this.password
87
+ }
88
+ }, (response) => {
89
+ if (response.type === 'auth_success') {
90
+ this.authenticated = true;
91
+ console.log('[Client] Authenticated successfully');
92
+ resolve();
93
+ } else if (response.type === 'error') {
94
+ reject(new Error(response.error));
95
+ }
96
+ });
97
+ });
98
+ }
99
+
100
+ async use(database) {
101
+ return new Promise((resolve, reject) => {
102
+ this._sendRequest({
103
+ type: 'use',
104
+ payload: { database }
105
+ }, (response) => {
106
+ if (response.type === 'use_success') {
107
+ this.currentDatabase = database;
108
+ console.log(`[Client] Using database '${database}'`);
109
+ resolve(response);
110
+ } else if (response.type === 'error') {
111
+ reject(new Error(response.error));
112
+ } else {
113
+ resolve(response);
114
+ }
115
+ });
116
+ });
117
+ }
118
+
119
+ async query(queryString, params = []) {
120
+ if (!this.connected) {
121
+ throw new Error('Not connected to server');
122
+ }
123
+
124
+ return new Promise((resolve, reject) => {
125
+ this._sendRequest({
126
+ type: 'query',
127
+ payload: {
128
+ query: queryString,
129
+ params: params
130
+ }
131
+ }, (response) => {
132
+ if (response.type === 'query_result') {
133
+ resolve(response.result);
134
+ } else if (response.type === 'error') {
135
+ reject(new Error(response.error));
136
+ } else {
137
+ resolve(response);
138
+ }
139
+ });
140
+ });
141
+ }
142
+
143
+ async listDatabases() {
144
+ return new Promise((resolve, reject) => {
145
+ this._sendRequest({
146
+ type: 'list_databases',
147
+ payload: {}
148
+ }, (response) => {
149
+ if (response.type === 'database_list') {
150
+ resolve(response.databases);
151
+ } else if (response.type === 'error') {
152
+ reject(new Error(response.error));
153
+ } else {
154
+ resolve(response);
155
+ }
156
+ });
157
+ });
158
+ }
159
+
160
+ async dropDatabase(database) {
161
+ return new Promise((resolve, reject) => {
162
+ this._sendRequest({
163
+ type: 'drop_database',
164
+ payload: { database }
165
+ }, (response) => {
166
+ if (response.type === 'drop_success') {
167
+ if (this.currentDatabase === database) {
168
+ this.currentDatabase = null;
169
+ }
170
+ resolve(response.message);
171
+ } else if (response.type === 'error') {
172
+ reject(new Error(response.error));
173
+ } else {
174
+ resolve(response);
175
+ }
176
+ });
177
+ });
178
+ }
179
+
180
+ async ping() {
181
+ return new Promise((resolve, reject) => {
182
+ const start = Date.now();
183
+ this._sendRequest({
184
+ type: 'ping',
185
+ payload: {}
186
+ }, (response) => {
187
+ if (response.type === 'pong') {
188
+ const latency = Date.now() - start;
189
+ resolve({ latency, serverTime: response.timestamp });
190
+ } else {
191
+ resolve(response);
192
+ }
193
+ });
194
+ });
195
+ }
196
+
197
+ async stats() {
198
+ return new Promise((resolve, reject) => {
199
+ this._sendRequest({
200
+ type: 'stats',
201
+ payload: {}
202
+ }, (response) => {
203
+ if (response.type === 'stats') {
204
+ resolve(response.stats);
205
+ } else if (response.type === 'error') {
206
+ reject(new Error(response.error));
207
+ } else {
208
+ resolve(response);
209
+ }
210
+ });
211
+ });
212
+ }
213
+
214
+ _sendRequest(request, callback) {
215
+ const id = ++this.requestId;
216
+ this.pendingRequests.push({ id, handler: callback });
217
+
218
+ try {
219
+ this.socket.write(JSON.stringify(request) + '\n');
220
+ } catch (err) {
221
+ console.error('[Client] Failed to send request:', err.message);
222
+ const idx = this.pendingRequests.findIndex(r => r.id === id);
223
+ if (idx !== -1) {
224
+ this.pendingRequests.splice(idx, 1);
225
+ }
226
+ throw err;
227
+ }
228
+ }
229
+
230
+ _handleData(data) {
231
+ this.buffer += data.toString();
232
+
233
+ let newlineIndex;
234
+ while ((newlineIndex = this.buffer.indexOf('\n')) !== -1) {
235
+ const message = this.buffer.substring(0, newlineIndex);
236
+ this.buffer = this.buffer.substring(newlineIndex + 1);
237
+
238
+ try {
239
+ const response = JSON.parse(message);
240
+ this._handleResponse(response);
241
+ } catch (err) {
242
+ console.error('[Client] Failed to parse response:', err.message);
243
+ }
244
+ }
245
+ }
246
+
247
+ _handleResponse(response) {
248
+ if (this.pendingRequests.length > 0) {
249
+ const request = this.pendingRequests.shift();
250
+ if (request.handler) {
251
+ request.handler(response);
252
+ }
253
+ }
254
+ }
255
+
256
+ disconnect() {
257
+ if (this.socket) {
258
+ this.socket.end();
259
+ this.socket = null;
260
+ this.connected = false;
261
+ }
262
+ }
263
+ }
264
+
265
+ module.exports = SawitClient;