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,297 @@
1
+ // @ts-nocheck
2
+ import { assertEquals, assertExists } from "jsr:@std/assert@1.0.14";
3
+ import { GunDB } from "../../core/database.ts";
4
+
5
+ const shouldRunMeshTests =
6
+ (Deno.env.get("RUN_MESH_TESTS") ?? "").toLowerCase() === "true";
7
+ const defaultTimeoutMs = Number(
8
+ Deno.env.get("RUN_MESH_TEST_TIMEOUT_MS") ?? "10000",
9
+ );
10
+
11
+ if (!shouldRunMeshTests) {
12
+ console.warn(
13
+ "[mesh-network.test] Skipping mesh networking integration tests. Set RUN_MESH_TESTS=true to enable.",
14
+ );
15
+ }
16
+
17
+ function withTimeout<T>(
18
+ promise: Promise<T>,
19
+ message: string,
20
+ timeoutMs = defaultTimeoutMs,
21
+ ): Promise<T> {
22
+ return new Promise((resolve, reject) => {
23
+ const timer = setTimeout(() => {
24
+ reject(new Error(message));
25
+ }, timeoutMs);
26
+
27
+ promise.then(
28
+ (value) => {
29
+ clearTimeout(timer);
30
+ resolve(value);
31
+ },
32
+ (error) => {
33
+ clearTimeout(timer);
34
+ reject(error);
35
+ },
36
+ );
37
+ });
38
+ }
39
+
40
+ function meshTest(name: string, fn: () => Promise<void>) {
41
+ Deno.test({
42
+ name,
43
+ ignore: !shouldRunMeshTests,
44
+ sanitizeOps: false,
45
+ sanitizeResources: false,
46
+ fn,
47
+ });
48
+ }
49
+
50
+ function randomPort(): number {
51
+ return 18000 + Math.floor(Math.random() * 10000);
52
+ }
53
+
54
+ meshTest("Mesh Network - Basic Connection and Sync", async () => {
55
+ const port = randomPort();
56
+ const serverUrl = `ws://localhost:${port}`;
57
+
58
+ const dbA = new GunDB();
59
+ const dbB = new GunDB();
60
+
61
+ try {
62
+ const kvA = await Deno.makeTempFile({ prefix: "kv_", suffix: ".sqlite" });
63
+ const kvB = await Deno.makeTempFile({ prefix: "kv_", suffix: ".sqlite" });
64
+
65
+ await dbA.ready(kvA);
66
+ await dbB.ready(kvB);
67
+
68
+ // Start server
69
+ await dbA.serve({ port });
70
+
71
+ // Add data to server
72
+ await dbA.put("mesh:test", {
73
+ text: "Hello from server A",
74
+ timestamp: Date.now(),
75
+ });
76
+
77
+ // Connect client and wait for sync
78
+ const receivedData = new Promise((resolve) => {
79
+ dbB.on("mesh:test", (node) => {
80
+ if (node && (node.data as any).text === "Hello from server A") {
81
+ resolve(true);
82
+ }
83
+ });
84
+ });
85
+
86
+ dbB.connect(serverUrl);
87
+ await withTimeout(receivedData as Promise<unknown>, "Timed out waiting for initial mesh sync");
88
+
89
+ // Verify data was received
90
+ const syncedData = await dbB.get("mesh:test");
91
+ assertExists(syncedData);
92
+ assertEquals((syncedData as any).text, "Hello from server A");
93
+ } finally {
94
+ await dbB.close();
95
+ await dbA.close();
96
+ }
97
+ });
98
+
99
+ meshTest("Mesh Network - Bidirectional Sync", async () => {
100
+ const port = randomPort();
101
+ const serverUrl = `ws://localhost:${port}`;
102
+
103
+ const dbA = new GunDB();
104
+ const dbB = new GunDB();
105
+
106
+ try {
107
+ const kvA = await Deno.makeTempFile({ prefix: "kv_", suffix: ".sqlite" });
108
+ const kvB = await Deno.makeTempFile({ prefix: "kv_", suffix: ".sqlite" });
109
+
110
+ await dbA.ready(kvA);
111
+ await dbB.ready(kvB);
112
+ await dbA.serve({ port });
113
+
114
+ // Connect B to A
115
+ dbB.connect(serverUrl);
116
+ await withTimeout(
117
+ new Promise((resolve) => setTimeout(resolve, 100)),
118
+ "Timed out waiting for mesh connection",
119
+ );
120
+
121
+ // A sends data to B
122
+ await dbA.put("from:A", { message: "Hello from A" });
123
+
124
+ const receivedFromA = new Promise((resolve) => {
125
+ dbB.on("from:A", (node) => {
126
+ if (node && (node.data as any).message === "Hello from A") {
127
+ resolve(true);
128
+ }
129
+ });
130
+ });
131
+ await withTimeout(
132
+ receivedFromA as Promise<unknown>,
133
+ "Timed out waiting for data from node A",
134
+ );
135
+
136
+ // B sends data to A
137
+ await dbB.put("from:B", { message: "Hello from B" });
138
+
139
+ const receivedFromB = new Promise((resolve) => {
140
+ dbA.on("from:B", (node) => {
141
+ if (node && (node.data as any).message === "Hello from B") {
142
+ resolve(true);
143
+ }
144
+ });
145
+ });
146
+ await withTimeout(
147
+ receivedFromB as Promise<unknown>,
148
+ "Timed out waiting for data from node B",
149
+ );
150
+
151
+ // Verify both received data
152
+ const dataFromA = await dbB.get("from:A");
153
+ const dataFromB = await dbA.get("from:B");
154
+
155
+ assertExists(dataFromA);
156
+ assertExists(dataFromB);
157
+ assertEquals((dataFromA as any).message, "Hello from A");
158
+ assertEquals((dataFromB as any).message, "Hello from B");
159
+ } finally {
160
+ await dbB.close();
161
+ await dbA.close();
162
+ }
163
+ });
164
+
165
+ meshTest("Mesh Network - Conflict Resolution", async () => {
166
+ const port = randomPort();
167
+ const serverUrl = `ws://localhost:${port}`;
168
+
169
+ const dbA = new GunDB();
170
+ const dbB = new GunDB();
171
+
172
+ try {
173
+ const kvA = await Deno.makeTempFile({ prefix: "kv_", suffix: ".sqlite" });
174
+ const kvB = await Deno.makeTempFile({ prefix: "kv_", suffix: ".sqlite" });
175
+
176
+ await dbA.ready(kvA);
177
+ await dbB.ready(kvB);
178
+ await dbA.serve({ port });
179
+
180
+ // Connect B to A
181
+ dbB.connect(serverUrl);
182
+ await new Promise((resolve) => setTimeout(resolve, 100));
183
+
184
+ // Both modify the same key simultaneously
185
+ const timestamp = Date.now();
186
+ await dbA.put("conflict:test", {
187
+ value: "from A",
188
+ timestamp: timestamp + 1000,
189
+ });
190
+ await dbB.put("conflict:test", {
191
+ value: "from B",
192
+ timestamp: timestamp + 2000,
193
+ });
194
+
195
+ // Wait for sync
196
+ await withTimeout(
197
+ new Promise((resolve) => setTimeout(resolve, 500)),
198
+ "Timed out waiting for conflict resolution",
199
+ );
200
+
201
+ // Check that both databases have the same final state
202
+ const finalA = await dbA.get("conflict:test");
203
+ const finalB = await dbB.get("conflict:test");
204
+
205
+ assertExists(finalA);
206
+ assertExists(finalB);
207
+
208
+ // Should have the newer timestamp (from B)
209
+ assertEquals((finalA as any).value, "from B");
210
+ assertEquals((finalB as any).value, "from B");
211
+ } finally {
212
+ await dbB.close();
213
+ await dbA.close();
214
+ }
215
+ });
216
+
217
+ meshTest("Mesh Network - Multiple Clients", async () => {
218
+ const port = randomPort();
219
+ const serverUrl = `ws://localhost:${port}`;
220
+
221
+ const dbA = new GunDB(); // Server
222
+ const dbB = new GunDB(); // Client 1
223
+ const dbC = new GunDB(); // Client 2
224
+
225
+ try {
226
+ const kvA = await Deno.makeTempFile({ prefix: "kv_", suffix: ".sqlite" });
227
+ const kvB = await Deno.makeTempFile({ prefix: "kv_", suffix: ".sqlite" });
228
+ const kvC = await Deno.makeTempFile({ prefix: "kv_", suffix: ".sqlite" });
229
+
230
+ await dbA.ready(kvA);
231
+ await dbB.ready(kvB);
232
+ await dbC.ready(kvC);
233
+ await dbA.serve({ port });
234
+
235
+ // Connect both clients
236
+ dbB.connect(serverUrl);
237
+ dbC.connect(serverUrl);
238
+ await new Promise((resolve) => setTimeout(resolve, 200));
239
+
240
+ // B sends data
241
+ await dbB.put("multi:test", { from: "B", message: "Hello from B" });
242
+
243
+ // Wait for both A and C to receive
244
+ const receivedOnA = new Promise((resolve) => {
245
+ dbA.on("multi:test", (node) => {
246
+ if (node && (node.data as any).from === "B") resolve(true);
247
+ });
248
+ });
249
+
250
+ const receivedOnC = new Promise((resolve) => {
251
+ dbC.on("multi:test", (node) => {
252
+ if (node && (node.data as any).from === "B") resolve(true);
253
+ });
254
+ });
255
+
256
+ await withTimeout(
257
+ Promise.all([receivedOnA, receivedOnC]),
258
+ "Timed out waiting for multi-client propagation",
259
+ );
260
+
261
+ // Verify all have the data
262
+ const dataA = await dbA.get("multi:test");
263
+ const dataB = await dbB.get("multi:test");
264
+ const dataC = await dbC.get("multi:test");
265
+
266
+ assertExists(dataA);
267
+ assertExists(dataB);
268
+ assertExists(dataC);
269
+ assertEquals((dataA as any).from, "B");
270
+ assertEquals((dataB as any).from, "B");
271
+ assertEquals((dataC as any).from, "B");
272
+ } finally {
273
+ await dbC.close();
274
+ await dbB.close();
275
+ await dbA.close();
276
+ }
277
+ });
278
+
279
+ meshTest("Mesh Network - Connection Error Handling", async () => {
280
+ const db = new GunDB();
281
+
282
+ try {
283
+ const kvPath = await Deno.makeTempFile({ prefix: "kv_", suffix: ".sqlite" });
284
+ await db.ready(kvPath);
285
+
286
+ // Try to connect to non-existent server
287
+ db.connect("ws://localhost:99999");
288
+
289
+ // Should not throw error, but connection should fail gracefully
290
+ await withTimeout(
291
+ new Promise((resolve) => setTimeout(resolve, 1000)),
292
+ "Timed out waiting for graceful connection failure",
293
+ );
294
+ } finally {
295
+ await db.close();
296
+ }
297
+ });
@@ -0,0 +1,30 @@
1
+ import { GunDB } from "../core/database.ts";
2
+ import type { Rule } from "../logic/rules.ts";
3
+
4
+ Deno.test("rule engine classification: Person.age >= 18 -> adult = true", async () => {
5
+ const db = new GunDB();
6
+ try {
7
+ const kvPath = await Deno.makeTempFile({ prefix: "kv_", suffix: ".sqlite" });
8
+ await db.ready(kvPath);
9
+
10
+ const rule: Rule = {
11
+ name: "adultClassifier",
12
+ whenType: "Person",
13
+ predicate: (node) =>
14
+ typeof (node.data as any).age === "number" && (node.data as any).age >= 18,
15
+ action: async (ctx, node) => {
16
+ const data = { ...(node.data as Record<string, unknown>), adult: true };
17
+ await ctx.db.put(node.id, data);
18
+ },
19
+ };
20
+ db.addRule(rule);
21
+
22
+ await db.put("p:alice", { name: "Alice", age: 20, type: "Person" });
23
+ const got = await db.get<{ adult?: boolean }>("p:alice");
24
+ if (!got || got.adult !== true) {
25
+ throw new Error("Expected adult flag set by rule");
26
+ }
27
+ } finally {
28
+ await db.close();
29
+ }
30
+ });
@@ -0,0 +1,288 @@
1
+ // @ts-nocheck
2
+ import { assertEquals, assertExists } from "jsr:@std/assert@1.0.14";
3
+ import { GunDB } from "../../core/database.ts";
4
+
5
+ interface PerformanceMetrics {
6
+ operation: string;
7
+ count: number;
8
+ totalTime: number;
9
+ averageTime: number;
10
+ operationsPerSecond: number;
11
+ }
12
+
13
+ function measureOperation(
14
+ operation: () => Promise<void>,
15
+ count: number,
16
+ ): Promise<PerformanceMetrics> {
17
+ return new Promise(async (resolve) => {
18
+ const startTime = performance.now();
19
+
20
+ for (let i = 0; i < count; i++) {
21
+ await operation();
22
+ }
23
+
24
+ const endTime = performance.now();
25
+ const totalTime = endTime - startTime;
26
+ const averageTime = totalTime / count;
27
+ const operationsPerSecond = (count / totalTime) * 1000;
28
+
29
+ resolve({
30
+ operation: "unknown",
31
+ count,
32
+ totalTime,
33
+ averageTime,
34
+ operationsPerSecond,
35
+ });
36
+ });
37
+ }
38
+
39
+ Deno.test("Performance - Bulk Insert Operations", async () => {
40
+ const db = new GunDB();
41
+ try {
42
+ const kvPath = await Deno.makeTempFile({
43
+ prefix: "kv_",
44
+ suffix: ".sqlite",
45
+ });
46
+ await db.ready(kvPath);
47
+
48
+ const count = 1000;
49
+ let currentCount = 0;
50
+
51
+ const metrics = await measureOperation(async () => {
52
+ await db.put(`perf:${currentCount++}`, {
53
+ id: currentCount,
54
+ data: `Performance test data ${currentCount}`,
55
+ timestamp: Date.now(),
56
+ });
57
+ }, count);
58
+
59
+ console.log(`Bulk Insert Performance:`, metrics);
60
+
61
+ // Verify all data was inserted
62
+ const allNodes = await db.getAll();
63
+ assertEquals(allNodes.length, count);
64
+
65
+ // Performance assertions
66
+ assertEquals(metrics.operationsPerSecond > 100, true); // At least 100 ops/sec
67
+ assertEquals(metrics.averageTime < 10, true); // Less than 10ms per operation
68
+ } finally {
69
+ await db.close();
70
+ }
71
+ });
72
+
73
+ Deno.test("Performance - Bulk Read Operations", async () => {
74
+ const db = new GunDB();
75
+ try {
76
+ const kvPath = await Deno.makeTempFile({
77
+ prefix: "kv_",
78
+ suffix: ".sqlite",
79
+ });
80
+ await db.ready(kvPath);
81
+
82
+ // Pre-populate with data
83
+ const count = 1000;
84
+ for (let i = 0; i < count; i++) {
85
+ await db.put(`read:${i}`, {
86
+ id: i,
87
+ data: `Read test data ${i}`,
88
+ timestamp: Date.now(),
89
+ });
90
+ }
91
+
92
+ let currentCount = 0;
93
+ const metrics = await measureOperation(async () => {
94
+ const data = await db.get(`read:${currentCount++}`);
95
+ assertExists(data);
96
+ }, count);
97
+
98
+ console.log(`Bulk Read Performance:`, metrics);
99
+
100
+ // Performance assertions
101
+ assertEquals(metrics.operationsPerSecond > 500, true); // At least 500 ops/sec
102
+ assertEquals(metrics.averageTime < 2, true); // Less than 2ms per operation
103
+ } finally {
104
+ await db.close();
105
+ }
106
+ });
107
+
108
+ Deno.test("Performance - Vector Search Operations", async () => {
109
+ const db = new GunDB();
110
+ try {
111
+ const kvPath = await Deno.makeTempFile({
112
+ prefix: "kv_",
113
+ suffix: ".sqlite",
114
+ });
115
+ await db.ready(kvPath);
116
+
117
+ // Pre-populate with documents for vector search
118
+ const count = 500;
119
+ for (let i = 0; i < count; i++) {
120
+ await db.put(`doc:${i}`, {
121
+ text: `Document ${i} about machine learning and artificial intelligence`,
122
+ content: `This is document number ${i} containing information about AI and ML algorithms`,
123
+ });
124
+ }
125
+
126
+ const searchQueries = [
127
+ "machine learning algorithms",
128
+ "artificial intelligence",
129
+ "neural networks",
130
+ "deep learning",
131
+ "data science",
132
+ ];
133
+
134
+ let queryCount = 0;
135
+ const metrics = await measureOperation(async () => {
136
+ const query = searchQueries[queryCount % searchQueries.length];
137
+ const results = await db.vectorSearch(query, 10);
138
+ assertExists(results);
139
+ queryCount++;
140
+ }, 100);
141
+
142
+ console.log(`Vector Search Performance:`, metrics);
143
+
144
+ // Performance assertions
145
+ assertEquals(metrics.operationsPerSecond > 50, true); // At least 50 ops/sec
146
+ assertEquals(metrics.averageTime < 20, true); // Less than 20ms per operation
147
+ } finally {
148
+ await db.close();
149
+ }
150
+ });
151
+
152
+ Deno.test("Performance - Concurrent Operations", async () => {
153
+ const db = new GunDB();
154
+ try {
155
+ const kvPath = await Deno.makeTempFile({
156
+ prefix: "kv_",
157
+ suffix: ".sqlite",
158
+ });
159
+ await db.ready(kvPath);
160
+
161
+ const concurrentCount = 100;
162
+ const operationsPerWorker = 10;
163
+
164
+ const startTime = performance.now();
165
+
166
+ // Run concurrent operations
167
+ const promises = Array.from({ length: concurrentCount }, async (_, i) => {
168
+ for (let j = 0; j < operationsPerWorker; j++) {
169
+ await db.put(`concurrent:${i}:${j}`, {
170
+ worker: i,
171
+ operation: j,
172
+ timestamp: Date.now(),
173
+ });
174
+ }
175
+ });
176
+
177
+ await Promise.all(promises);
178
+
179
+ const endTime = performance.now();
180
+ const totalTime = endTime - startTime;
181
+ const totalOperations = concurrentCount * operationsPerWorker;
182
+ const operationsPerSecond = (totalOperations / totalTime) * 1000;
183
+
184
+ console.log(`Concurrent Operations Performance:`, {
185
+ totalOperations,
186
+ totalTime,
187
+ operationsPerSecond,
188
+ });
189
+
190
+ // Verify all operations completed
191
+ const allNodes = await db.getAll();
192
+ assertEquals(allNodes.length, totalOperations);
193
+
194
+ // Performance assertions
195
+ assertEquals(operationsPerSecond > 200, true); // At least 200 ops/sec
196
+ } finally {
197
+ await db.close();
198
+ }
199
+ });
200
+
201
+ Deno.test("Performance - Memory Usage", async () => {
202
+ const db = new GunDB();
203
+ try {
204
+ const kvPath = await Deno.makeTempFile({
205
+ prefix: "kv_",
206
+ suffix: ".sqlite",
207
+ });
208
+ await db.ready(kvPath);
209
+
210
+ const initialMemory = (performance as any).memory?.usedJSHeapSize || 0;
211
+
212
+ // Insert large amount of data
213
+ const count = 10000;
214
+ for (let i = 0; i < count; i++) {
215
+ await db.put(`memory:${i}`, {
216
+ id: i,
217
+ largeData: "x".repeat(1000), // 1KB per record
218
+ timestamp: Date.now(),
219
+ });
220
+ }
221
+
222
+ const afterInsertMemory = (performance as any).memory?.usedJSHeapSize || 0;
223
+ const memoryIncrease = afterInsertMemory - initialMemory;
224
+
225
+ console.log(`Memory Usage:`, {
226
+ initialMemory,
227
+ afterInsertMemory,
228
+ memoryIncrease,
229
+ memoryPerRecord: memoryIncrease / count,
230
+ });
231
+
232
+ // Memory efficiency assertions
233
+ assertEquals(memoryIncrease < 50 * 1024 * 1024, true); // Less than 50MB increase
234
+ assertEquals(memoryIncrease / count < 5000, true); // Less than 5KB per record
235
+ } finally {
236
+ await db.close();
237
+ }
238
+ });
239
+
240
+ Deno.test("Performance - Subscription Performance", async () => {
241
+ const db = new GunDB();
242
+ try {
243
+ const kvPath = await Deno.makeTempFile({
244
+ prefix: "kv_",
245
+ suffix: ".sqlite",
246
+ });
247
+ await db.ready(kvPath);
248
+
249
+ const subscriptionCount = 1000;
250
+ const updateCount = 100;
251
+
252
+ // Set up many subscriptions
253
+ const subscriptions: Array<() => void> = [];
254
+ for (let i = 0; i < subscriptionCount; i++) {
255
+ const unsubscribe = db.on(`sub:${i}`, () => {});
256
+ subscriptions.push(unsubscribe);
257
+ }
258
+
259
+ const startTime = performance.now();
260
+
261
+ // Trigger updates
262
+ for (let i = 0; i < updateCount; i++) {
263
+ await db.put(`sub:${i % subscriptionCount}`, {
264
+ update: i,
265
+ timestamp: Date.now(),
266
+ });
267
+ }
268
+
269
+ const endTime = performance.now();
270
+ const totalTime = endTime - startTime;
271
+ const updatesPerSecond = (updateCount / totalTime) * 1000;
272
+
273
+ console.log(`Subscription Performance:`, {
274
+ subscriptionCount,
275
+ updateCount,
276
+ totalTime,
277
+ updatesPerSecond,
278
+ });
279
+
280
+ // Clean up subscriptions
281
+ subscriptions.forEach((unsubscribe) => unsubscribe());
282
+
283
+ // Performance assertions
284
+ assertEquals(updatesPerSecond > 50, true); // At least 50 updates/sec
285
+ } finally {
286
+ await db.close();
287
+ }
288
+ });