pluresdb 1.0.1

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 (84) hide show
  1. package/LICENSE +72 -0
  2. package/README.md +322 -0
  3. package/dist/.tsbuildinfo +1 -0
  4. package/dist/cli.d.ts +7 -0
  5. package/dist/cli.d.ts.map +1 -0
  6. package/dist/cli.js +253 -0
  7. package/dist/cli.js.map +1 -0
  8. package/dist/node-index.d.ts +52 -0
  9. package/dist/node-index.d.ts.map +1 -0
  10. package/dist/node-index.js +359 -0
  11. package/dist/node-index.js.map +1 -0
  12. package/dist/node-wrapper.d.ts +44 -0
  13. package/dist/node-wrapper.d.ts.map +1 -0
  14. package/dist/node-wrapper.js +294 -0
  15. package/dist/node-wrapper.js.map +1 -0
  16. package/dist/types/index.d.ts +28 -0
  17. package/dist/types/index.d.ts.map +1 -0
  18. package/dist/types/index.js +3 -0
  19. package/dist/types/index.js.map +1 -0
  20. package/dist/types/node-types.d.ts +59 -0
  21. package/dist/types/node-types.d.ts.map +1 -0
  22. package/dist/types/node-types.js +6 -0
  23. package/dist/types/node-types.js.map +1 -0
  24. package/dist/vscode/extension.d.ts +81 -0
  25. package/dist/vscode/extension.d.ts.map +1 -0
  26. package/dist/vscode/extension.js +309 -0
  27. package/dist/vscode/extension.js.map +1 -0
  28. package/examples/basic-usage.d.ts +2 -0
  29. package/examples/basic-usage.d.ts.map +1 -0
  30. package/examples/basic-usage.js +26 -0
  31. package/examples/basic-usage.js.map +1 -0
  32. package/examples/basic-usage.ts +29 -0
  33. package/examples/vscode-extension-example/README.md +95 -0
  34. package/examples/vscode-extension-example/package.json +49 -0
  35. package/examples/vscode-extension-example/src/extension.ts +163 -0
  36. package/examples/vscode-extension-example/tsconfig.json +12 -0
  37. package/examples/vscode-extension-integration.d.ts +24 -0
  38. package/examples/vscode-extension-integration.d.ts.map +1 -0
  39. package/examples/vscode-extension-integration.js +285 -0
  40. package/examples/vscode-extension-integration.js.map +1 -0
  41. package/examples/vscode-extension-integration.ts +41 -0
  42. package/package.json +115 -0
  43. package/scripts/compiled-crud-verify.ts +28 -0
  44. package/scripts/dogfood.ts +258 -0
  45. package/scripts/postinstall.js +155 -0
  46. package/scripts/run-tests.ts +175 -0
  47. package/scripts/setup-libclang.ps1 +209 -0
  48. package/src/benchmarks/memory-benchmarks.ts +316 -0
  49. package/src/benchmarks/run-benchmarks.ts +293 -0
  50. package/src/cli.ts +231 -0
  51. package/src/config.ts +49 -0
  52. package/src/core/crdt.ts +104 -0
  53. package/src/core/database.ts +494 -0
  54. package/src/healthcheck.ts +156 -0
  55. package/src/http/api-server.ts +334 -0
  56. package/src/index.ts +28 -0
  57. package/src/logic/rules.ts +44 -0
  58. package/src/main.rs +3 -0
  59. package/src/main.ts +190 -0
  60. package/src/network/websocket-server.ts +115 -0
  61. package/src/node-index.ts +385 -0
  62. package/src/node-wrapper.ts +320 -0
  63. package/src/sqlite-compat.ts +586 -0
  64. package/src/sqlite3-compat.ts +55 -0
  65. package/src/storage/kv-storage.ts +71 -0
  66. package/src/tests/core.test.ts +281 -0
  67. package/src/tests/fixtures/performance-data.json +71 -0
  68. package/src/tests/fixtures/test-data.json +124 -0
  69. package/src/tests/integration/api-server.test.ts +232 -0
  70. package/src/tests/integration/mesh-network.test.ts +297 -0
  71. package/src/tests/logic.test.ts +30 -0
  72. package/src/tests/performance/load.test.ts +288 -0
  73. package/src/tests/security/input-validation.test.ts +282 -0
  74. package/src/tests/unit/core.test.ts +216 -0
  75. package/src/tests/unit/subscriptions.test.ts +135 -0
  76. package/src/tests/unit/vector-search.test.ts +173 -0
  77. package/src/tests/vscode_extension_test.ts +253 -0
  78. package/src/types/index.ts +32 -0
  79. package/src/types/node-types.ts +66 -0
  80. package/src/util/debug.ts +14 -0
  81. package/src/vector/index.ts +59 -0
  82. package/src/vscode/extension.ts +364 -0
  83. package/web/README.md +27 -0
  84. package/web/svelte/package.json +31 -0
@@ -0,0 +1,586 @@
1
+ // SQLite Compatible API for PluresDB
2
+ // Provides drop-in replacement for sqlite3 and sqlite packages
3
+
4
+ import { dirname, resolve } from "node:path";
5
+
6
+ import { PluresNode } from "./node-wrapper.ts";
7
+
8
+ export interface SQLiteConfig {
9
+ filename: string;
10
+ driver: any; // SQLite driver (ignored, but kept for compatibility)
11
+ mode?: number;
12
+ verbose?: boolean;
13
+ }
14
+
15
+ export interface DatabaseOptions {
16
+ filename: string;
17
+ driver?: any;
18
+ mode?: number;
19
+ verbose?: boolean;
20
+ }
21
+
22
+ type RowRecord = Record<string, unknown>;
23
+
24
+ export class Database {
25
+ private plures: PluresNode;
26
+ private filename: string;
27
+ private isOpen: boolean = false;
28
+ private verbose: boolean = false;
29
+
30
+ constructor(options: DatabaseOptions) {
31
+ this.filename = options.filename;
32
+ this.verbose = options.verbose || false;
33
+
34
+ // Initialize PluresDB with the SQLite file path
35
+ const dataDir = resolve(dirname(options.filename), "pluresdb");
36
+ this.plures = new PluresNode({
37
+ config: {
38
+ dataDir,
39
+ port: 34567,
40
+ host: "localhost",
41
+ },
42
+ });
43
+ }
44
+
45
+ async open(): Promise<Database> {
46
+ if (this.isOpen) {
47
+ return this;
48
+ }
49
+
50
+ try {
51
+ await this.plures.start();
52
+ this.isOpen = true;
53
+
54
+ if (this.verbose) {
55
+ console.log(`Database opened: ${this.filename}`);
56
+ }
57
+
58
+ return this;
59
+ } catch (error) {
60
+ throw new Error(`Failed to open database: ${formatError(error)}`);
61
+ }
62
+ }
63
+
64
+ async close(): Promise<void> {
65
+ if (!this.isOpen) {
66
+ return;
67
+ }
68
+
69
+ try {
70
+ await this.plures.stop();
71
+ this.isOpen = false;
72
+
73
+ if (this.verbose) {
74
+ console.log(`Database closed: ${this.filename}`);
75
+ }
76
+ } catch (error) {
77
+ throw new Error(`Failed to close database: ${formatError(error)}`);
78
+ }
79
+ }
80
+
81
+ // SQLite-compatible methods
82
+ async exec(sql: string): Promise<void> {
83
+ if (!this.isOpen) {
84
+ throw new Error("Database is not open");
85
+ }
86
+
87
+ try {
88
+ // Parse SQL and execute
89
+ const statements = this.parseSQL(sql);
90
+
91
+ for (const statement of statements) {
92
+ await this.executeStatement(statement);
93
+ }
94
+ } catch (error) {
95
+ throw new Error(`SQL execution failed: ${formatError(error)}`);
96
+ }
97
+ }
98
+
99
+ async run(sql: string, params: any[] = []): Promise<{ lastID: number; changes: number }> {
100
+ if (!this.isOpen) {
101
+ throw new Error("Database is not open");
102
+ }
103
+
104
+ try {
105
+ const result = await this.executeStatement({ sql, params });
106
+ return {
107
+ lastID: result.lastID || 0,
108
+ changes: result.changes || 0,
109
+ };
110
+ } catch (error) {
111
+ throw new Error(`SQL run failed: ${formatError(error)}`);
112
+ }
113
+ }
114
+
115
+ async get(sql: string, params: any[] = []): Promise<any> {
116
+ if (!this.isOpen) {
117
+ throw new Error("Database is not open");
118
+ }
119
+
120
+ try {
121
+ const results = await this.executeQuery(sql, params);
122
+ return results.length > 0 ? results[0] : undefined;
123
+ } catch (error) {
124
+ throw new Error(`SQL get failed: ${formatError(error)}`);
125
+ }
126
+ }
127
+
128
+ async all(sql: string, params: any[] = []): Promise<any[]> {
129
+ if (!this.isOpen) {
130
+ throw new Error("Database is not open");
131
+ }
132
+
133
+ try {
134
+ return await this.executeQuery(sql, params);
135
+ } catch (error) {
136
+ throw new Error(`SQL all failed: ${formatError(error)}`);
137
+ }
138
+ }
139
+
140
+ async each(sql: string, params: any[] = [], callback: (row: any) => void): Promise<number> {
141
+ if (!this.isOpen) {
142
+ throw new Error("Database is not open");
143
+ }
144
+
145
+ try {
146
+ const results = await this.executeQuery(sql, params);
147
+ let count = 0;
148
+
149
+ for (const row of results) {
150
+ callback(row);
151
+ count++;
152
+ }
153
+
154
+ return count;
155
+ } catch (error) {
156
+ throw new Error(`SQL each failed: ${formatError(error)}`);
157
+ }
158
+ }
159
+
160
+ // Transaction support
161
+ async transaction<T>(fn: (db: Database) => Promise<T>): Promise<T> {
162
+ if (!this.isOpen) {
163
+ throw new Error("Database is not open");
164
+ }
165
+
166
+ try {
167
+ await this.exec("BEGIN TRANSACTION");
168
+ const result = await fn(this);
169
+ await this.exec("COMMIT");
170
+ return result;
171
+ } catch (error) {
172
+ await this.exec("ROLLBACK");
173
+ throw error;
174
+ }
175
+ }
176
+
177
+ // Prepare statements (simplified implementation)
178
+ prepare(sql: string): PreparedStatement {
179
+ return new PreparedStatement(this, sql);
180
+ }
181
+
182
+ // Private helper methods
183
+ private parseSQL(sql: string): Array<{ sql: string; params?: any[] }> {
184
+ // Simple SQL parser - split by semicolon and trim
185
+ const statements = sql
186
+ .split(";")
187
+ .map((s) => s.trim())
188
+ .filter((s) => s.length > 0);
189
+
190
+ return statements.map((statement) => ({ sql: statement }));
191
+ }
192
+
193
+ private async executeStatement(statement: { sql: string; params?: any[] }): Promise<any> {
194
+ const sql = statement.sql.toLowerCase().trim();
195
+
196
+ if (sql.startsWith("create table")) {
197
+ return await this.createTable(statement.sql);
198
+ } else if (sql.startsWith("drop table")) {
199
+ return await this.dropTable(statement.sql);
200
+ } else if (sql.startsWith("insert")) {
201
+ return await this.insert(statement.sql, statement.params || []);
202
+ } else if (sql.startsWith("update")) {
203
+ return await this.update(statement.sql, statement.params || []);
204
+ } else if (sql.startsWith("delete")) {
205
+ return await this.delete(statement.sql, statement.params || []);
206
+ } else if (sql.startsWith("begin") || sql.startsWith("commit") || sql.startsWith("rollback")) {
207
+ // Transaction commands - handled by transaction method
208
+ return { changes: 0 };
209
+ } else {
210
+ throw new Error(`Unsupported SQL statement: ${statement.sql}`);
211
+ }
212
+ }
213
+
214
+ private async executeQuery(sql: string, params: any[]): Promise<RowRecord[]> {
215
+ const sqlLower = sql.toLowerCase().trim();
216
+
217
+ if (sqlLower.startsWith("select")) {
218
+ return await this.select(sql, params);
219
+ } else {
220
+ throw new Error(`Unsupported query: ${sql}`);
221
+ }
222
+ }
223
+
224
+ private async createTable(sql: string): Promise<void> {
225
+ // Extract table name and columns from CREATE TABLE statement
226
+ const tableMatch = sql.match(/CREATE TABLE\s+(?:IF NOT EXISTS\s+)?(\w+)\s*\(([^)]+)\)/i);
227
+ if (!tableMatch) {
228
+ throw new Error(`Invalid CREATE TABLE statement: ${sql}`);
229
+ }
230
+
231
+ const tableName = tableMatch[1];
232
+ const columns = tableMatch[2].split(",").map((col) => col.trim());
233
+
234
+ // Store table schema in PluresDB
235
+ await this.plures.put(`schema:${tableName}`, {
236
+ name: tableName,
237
+ columns: columns,
238
+ created_at: new Date().toISOString(),
239
+ });
240
+
241
+ if (this.verbose) {
242
+ console.log(`Table created: ${tableName}`);
243
+ }
244
+ }
245
+
246
+ private async dropTable(sql: string): Promise<void> {
247
+ const tableMatch = sql.match(/DROP TABLE\s+(?:IF EXISTS\s+)?(\w+)/i);
248
+ if (!tableMatch) {
249
+ throw new Error(`Invalid DROP TABLE statement: ${sql}`);
250
+ }
251
+
252
+ const tableName = tableMatch[1];
253
+
254
+ // Remove table schema and all data
255
+ await this.plures.delete(`schema:${tableName}`);
256
+
257
+ // Delete all rows for this table
258
+ const rows: RowRecord[] = await this.plures.query(`table:${tableName}:*`);
259
+ for (const row of rows) {
260
+ await this.plures.delete(getRowId(row));
261
+ }
262
+
263
+ if (this.verbose) {
264
+ console.log(`Table dropped: ${tableName}`);
265
+ }
266
+ }
267
+
268
+ private async insert(sql: string, params: any[]): Promise<{ lastID: number; changes: number }> {
269
+ const insertMatch = sql.match(
270
+ /INSERT\s+(?:INTO\s+)?(\w+)\s*\(([^)]+)\)\s*VALUES\s*\(([^)]+)\)/i,
271
+ );
272
+ if (!insertMatch) {
273
+ throw new Error(`Invalid INSERT statement: ${sql}`);
274
+ }
275
+
276
+ const tableName = insertMatch[1];
277
+ const columns = insertMatch[2].split(",").map((col) => col.trim());
278
+ const values = insertMatch[3].split(",").map((val) => val.trim());
279
+
280
+ // Replace ? placeholders with actual values
281
+ const actualValues = values.map((val, index) => {
282
+ if (val === "?") {
283
+ return params[index] || null;
284
+ } else if (val.startsWith("'") && val.endsWith("'")) {
285
+ return val.slice(1, -1); // Remove quotes
286
+ } else {
287
+ return val;
288
+ }
289
+ });
290
+
291
+ // Create row object
292
+ const row: any = {};
293
+ columns.forEach((col, index) => {
294
+ row[col] = actualValues[index];
295
+ });
296
+
297
+ // Generate unique ID
298
+ const id = `${tableName}:${Date.now()}:${Math.random().toString(36).substr(2, 9)}`;
299
+ row.id = id;
300
+ row.created_at = new Date().toISOString();
301
+
302
+ // Store in PluresDB
303
+ await this.plures.put(`table:${tableName}:${id}`, row);
304
+
305
+ if (this.verbose) {
306
+ console.log(`Row inserted into ${tableName}:`, row);
307
+ }
308
+
309
+ return { lastID: 0, changes: 1 };
310
+ }
311
+
312
+ private async update(sql: string, params: any[]): Promise<{ changes: number }> {
313
+ const updateMatch = sql.match(/UPDATE\s+(\w+)\s+SET\s+([^WHERE]+)(?:\s+WHERE\s+(.+))?/i);
314
+ if (!updateMatch) {
315
+ throw new Error(`Invalid UPDATE statement: ${sql}`);
316
+ }
317
+
318
+ const tableName = updateMatch[1];
319
+ const setClause = updateMatch[2];
320
+ const whereClause = updateMatch[3];
321
+
322
+ // Parse SET clause
323
+ const setPairs = setClause.split(",").map((pair) => pair.trim());
324
+ const updates: any = {};
325
+
326
+ setPairs.forEach((pair, index) => {
327
+ const [column, value] = pair.split("=").map((s) => s.trim());
328
+ if (value === "?") {
329
+ updates[column] = params[index] || null;
330
+ } else if (value.startsWith("'") && value.endsWith("'")) {
331
+ updates[column] = value.slice(1, -1);
332
+ } else {
333
+ updates[column] = value;
334
+ }
335
+ });
336
+
337
+ // Find rows to update
338
+ const rows: RowRecord[] = await this.plures.query(`table:${tableName}:*`);
339
+ let changes = 0;
340
+
341
+ for (const row of rows) {
342
+ if (whereClause) {
343
+ // Simple WHERE clause evaluation (basic implementation)
344
+ if (this.evaluateWhereClause(row, whereClause, params)) {
345
+ const updatedRow = { ...row, ...updates, updated_at: new Date().toISOString() };
346
+ await this.plures.put(getRowId(row), updatedRow);
347
+ changes++;
348
+ }
349
+ } else {
350
+ // Update all rows
351
+ const updatedRow = { ...row, ...updates, updated_at: new Date().toISOString() };
352
+ await this.plures.put(getRowId(row), updatedRow);
353
+ changes++;
354
+ }
355
+ }
356
+
357
+ if (this.verbose) {
358
+ console.log(`Updated ${changes} rows in ${tableName}`);
359
+ }
360
+
361
+ return { changes };
362
+ }
363
+
364
+ private async delete(sql: string, params: any[]): Promise<{ changes: number }> {
365
+ const deleteMatch = sql.match(/DELETE\s+FROM\s+(\w+)(?:\s+WHERE\s+(.+))?/i);
366
+ if (!deleteMatch) {
367
+ throw new Error(`Invalid DELETE statement: ${sql}`);
368
+ }
369
+
370
+ const tableName = deleteMatch[1];
371
+ const whereClause = deleteMatch[2];
372
+
373
+ // Find rows to delete
374
+ const rows: RowRecord[] = await this.plures.query(`table:${tableName}:*`);
375
+ let changes = 0;
376
+
377
+ for (const row of rows) {
378
+ if (whereClause) {
379
+ // Simple WHERE clause evaluation
380
+ if (this.evaluateWhereClause(row, whereClause, params)) {
381
+ await this.plures.delete(getRowId(row));
382
+ changes++;
383
+ }
384
+ } else {
385
+ // Delete all rows
386
+ await this.plures.delete(getRowId(row));
387
+ changes++;
388
+ }
389
+ }
390
+
391
+ if (this.verbose) {
392
+ console.log(`Deleted ${changes} rows from ${tableName}`);
393
+ }
394
+
395
+ return { changes };
396
+ }
397
+
398
+ private async select(sql: string, params: any[]): Promise<RowRecord[]> {
399
+ const selectMatch = sql.match(
400
+ /SELECT\s+(.+?)\s+FROM\s+(\w+)(?:\s+WHERE\s+(.+))?(?:\s+ORDER\s+BY\s+(.+))?(?:\s+LIMIT\s+(\d+))?/i,
401
+ );
402
+ if (!selectMatch) {
403
+ throw new Error(`Invalid SELECT statement: ${sql}`);
404
+ }
405
+
406
+ const columns = selectMatch[1];
407
+ const tableName = selectMatch[2];
408
+ const whereClause = selectMatch[3];
409
+ const orderBy = selectMatch[4];
410
+ const limit = selectMatch[5] ? parseInt(selectMatch[5]) : undefined;
411
+
412
+ // Get all rows for the table
413
+ const rows: RowRecord[] = await this.plures.query(`table:${tableName}:*`);
414
+ let results: RowRecord[] = rows;
415
+
416
+ // Apply WHERE clause
417
+ if (whereClause) {
418
+ results = results.filter((row) => this.evaluateWhereClause(row, whereClause, params));
419
+ }
420
+
421
+ // Apply ORDER BY
422
+ if (orderBy) {
423
+ const [column, direction] = orderBy.split(/\s+/);
424
+ const isDesc = direction?.toLowerCase() === "desc";
425
+ results.sort((a, b) => compareValues(a[column], b[column], isDesc));
426
+ }
427
+
428
+ // Apply LIMIT
429
+ if (limit) {
430
+ results = results.slice(0, limit);
431
+ }
432
+
433
+ // Select specific columns
434
+ if (columns !== "*") {
435
+ const columnList = columns.split(",").map((col) => col.trim());
436
+ results = results.map((row) => {
437
+ const selectedRow: RowRecord = {};
438
+ columnList.forEach((col) => {
439
+ selectedRow[col] = row[col];
440
+ });
441
+ return selectedRow;
442
+ });
443
+ }
444
+
445
+ if (this.verbose) {
446
+ console.log(`Selected ${results.length} rows from ${tableName}`);
447
+ }
448
+
449
+ return results;
450
+ }
451
+
452
+ private evaluateWhereClause(row: RowRecord, whereClause: string, params: any[]): boolean {
453
+ // Simple WHERE clause evaluation
454
+ // This is a basic implementation - in production, you'd want a proper SQL parser
455
+
456
+ // Handle simple equality comparisons
457
+ const equalityMatch = whereClause.match(/(\w+)\s*=\s*\?/);
458
+ if (equalityMatch) {
459
+ const column = equalityMatch[1];
460
+ const value = params[0];
461
+ return row[column] === value;
462
+ }
463
+
464
+ // Handle string equality
465
+ const stringMatch = whereClause.match(/(\w+)\s*=\s*'([^']+)'/);
466
+ if (stringMatch) {
467
+ const column = stringMatch[1];
468
+ const value = stringMatch[2];
469
+ return row[column] === value;
470
+ }
471
+
472
+ // Default to true for unsupported WHERE clauses
473
+ return true;
474
+ }
475
+ }
476
+
477
+ function formatError(error: unknown): string {
478
+ if (error instanceof Error) {
479
+ return error.message;
480
+ }
481
+ if (typeof error === "string") {
482
+ return error;
483
+ }
484
+ try {
485
+ return JSON.stringify(error);
486
+ } catch {
487
+ return "Unknown error";
488
+ }
489
+ }
490
+
491
+ function getRowId(row: RowRecord): string {
492
+ const id = row["id"];
493
+ if (typeof id === "string" && id.length > 0) {
494
+ return id;
495
+ }
496
+ throw new Error("Row is missing a valid string id");
497
+ }
498
+
499
+ function compareValues(a: unknown, b: unknown, desc = false): number {
500
+ const normalize = (value: unknown): string => {
501
+ if (value === null || value === undefined) {
502
+ return "";
503
+ }
504
+ if (typeof value === "string") {
505
+ return value;
506
+ }
507
+ if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") {
508
+ return value.toString();
509
+ }
510
+ try {
511
+ return JSON.stringify(value);
512
+ } catch {
513
+ return String(value);
514
+ }
515
+ };
516
+
517
+ const normalizedA = normalize(a);
518
+ const normalizedB = normalize(b);
519
+ const result = normalizedA.localeCompare(normalizedB, undefined, {
520
+ numeric: true,
521
+ sensitivity: "base",
522
+ });
523
+
524
+ return desc ? -result : result;
525
+ }
526
+
527
+ export class PreparedStatement {
528
+ private db: Database;
529
+ private sql: string;
530
+
531
+ constructor(db: Database, sql: string) {
532
+ this.db = db;
533
+ this.sql = sql;
534
+ }
535
+
536
+ async run(params: any[] = []): Promise<{ lastID: number; changes: number }> {
537
+ return await this.db.run(this.sql, params);
538
+ }
539
+
540
+ async get(params: any[] = []): Promise<any> {
541
+ return await this.db.get(this.sql, params);
542
+ }
543
+
544
+ async all(params: any[] = []): Promise<any[]> {
545
+ return await this.db.all(this.sql, params);
546
+ }
547
+
548
+ async each(params: any[] = [], callback: (row: any) => void): Promise<number> {
549
+ return await this.db.each(this.sql, params, callback);
550
+ }
551
+
552
+ finalize(): void {
553
+ // No-op for compatibility
554
+ }
555
+ }
556
+
557
+ // SQLite3 driver compatibility
558
+ export class SQLite3Database extends Database {
559
+ constructor(filename: string, callback?: (err: Error | null) => void) {
560
+ super({ filename });
561
+
562
+ if (callback) {
563
+ this.open()
564
+ .then(() => callback(null))
565
+ .catch((err) => callback(err));
566
+ }
567
+ }
568
+
569
+ static Database = Database;
570
+ static OPEN_READONLY = 1;
571
+ static OPEN_READWRITE = 2;
572
+ static OPEN_CREATE = 4;
573
+ static OPEN_FULLMUTEX = 0x00010000;
574
+ static OPEN_SHAREDCACHE = 0x00020000;
575
+ static OPEN_PRIVATECACHE = 0x00040000;
576
+ static OPEN_URI = 0x00000040;
577
+ }
578
+
579
+ // Main export function - SQLite compatible API
580
+ export async function open(options: SQLiteConfig): Promise<Database> {
581
+ const db = new Database(options);
582
+ return await db.open();
583
+ }
584
+
585
+ // Export for compatibility with sqlite package
586
+ export { Database as default };
@@ -0,0 +1,55 @@
1
+ // SQLite3 Compatible API for PluresDB
2
+ // Provides drop-in replacement for sqlite3 package
3
+
4
+ import { Database as PluresDBDatabase } from "./sqlite-compat.ts";
5
+
6
+ export class Database extends PluresDBDatabase {
7
+ constructor(filename: string, callback?: (err: Error | null) => void) {
8
+ super({ filename });
9
+
10
+ if (callback) {
11
+ this.open()
12
+ .then(() => callback(null))
13
+ .catch((err) => callback(err));
14
+ }
15
+ }
16
+
17
+ // SQLite3 specific methods
18
+ serialize(callback?: (err: Error | null, sql: string) => void): void {
19
+ if (callback) {
20
+ callback(null, "-- Serialization not supported in PluresDB");
21
+ }
22
+ }
23
+
24
+ parallelize(callback?: (err: Error | null) => void): void {
25
+ if (callback) {
26
+ callback(null);
27
+ }
28
+ }
29
+
30
+ configure(option: string, value: any): void {
31
+ // No-op for compatibility
32
+ }
33
+
34
+ interrupt(): void {
35
+ // No-op for compatibility
36
+ }
37
+
38
+ loadExtension(path: string, callback?: (err: Error | null) => void): void {
39
+ if (callback) {
40
+ callback(new Error("Extensions not supported in PluresDB"), null);
41
+ }
42
+ }
43
+ }
44
+
45
+ // Export constants for compatibility
46
+ export const OPEN_READONLY = 1;
47
+ export const OPEN_READWRITE = 2;
48
+ export const OPEN_CREATE = 4;
49
+ export const OPEN_FULLMUTEX = 0x00010000;
50
+ export const OPEN_SHAREDCACHE = 0x00020000;
51
+ export const OPEN_PRIVATECACHE = 0x00040000;
52
+ export const OPEN_URI = 0x00000040;
53
+
54
+ // Export Database as default
55
+ export default Database;
@@ -0,0 +1,71 @@
1
+ import type { NodeRecord } from "../types/index.ts";
2
+
3
+ export class KvStorage {
4
+ private kv: Deno.Kv | null = null;
5
+
6
+ async open(path?: string): Promise<void> {
7
+ this.kv = await Deno.openKv(path);
8
+ }
9
+
10
+ async close(): Promise<void> {
11
+ if (this.kv) {
12
+ try {
13
+ this.kv.close();
14
+ } catch {
15
+ /* ignore */
16
+ }
17
+ this.kv = null;
18
+ }
19
+ // Allow microtasks to flush for callers awaiting close()
20
+ await Promise.resolve();
21
+ }
22
+
23
+ private ensureKv(): Deno.Kv {
24
+ if (!this.kv) {
25
+ throw new Error("KvStorage is not opened. Call open() first.");
26
+ }
27
+ return this.kv;
28
+ }
29
+
30
+ async getNode(id: string): Promise<NodeRecord | null> {
31
+ const kv = this.ensureKv();
32
+ const res = await kv.get<NodeRecord>(["node", id]);
33
+ return res.value ?? null;
34
+ }
35
+
36
+ async setNode(node: NodeRecord): Promise<void> {
37
+ const kv = this.ensureKv();
38
+ await kv.set(["node", node.id], node);
39
+
40
+ // Store version history
41
+ const historyKey = ["history", node.id, node.timestamp];
42
+ await kv.set(historyKey, node);
43
+ }
44
+
45
+ async deleteNode(id: string): Promise<void> {
46
+ const kv = this.ensureKv();
47
+ await kv.delete(["node", id]);
48
+ }
49
+
50
+ async *listNodes(): AsyncIterable<NodeRecord> {
51
+ const kv = this.ensureKv();
52
+ for await (const entry of kv.list<NodeRecord>({ prefix: ["node"] })) {
53
+ if (entry.value) yield entry.value;
54
+ }
55
+ }
56
+
57
+ async *listNodeHistory(id: string): AsyncIterable<NodeRecord> {
58
+ const kv = this.ensureKv();
59
+ for await (const entry of kv.list<NodeRecord>({ prefix: ["history", id] })) {
60
+ if (entry.value) yield entry.value;
61
+ }
62
+ }
63
+
64
+ async getNodeHistory(id: string): Promise<NodeRecord[]> {
65
+ const history: NodeRecord[] = [];
66
+ for await (const version of this.listNodeHistory(id)) {
67
+ history.push(version);
68
+ }
69
+ return history.sort((a, b) => b.timestamp - a.timestamp); // Most recent first
70
+ }
71
+ }