@plures/pluresdb 1.6.10 → 2.9.6
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.
- package/README.md +97 -289
- package/crates/README.md +99 -0
- package/crates/pluresdb-node/README.md +181 -0
- package/crates/pluresdb-node/index.d.ts +0 -0
- package/crates/pluresdb-node/index.js +265 -0
- package/crates/pluresdb-node/package.json +35 -0
- package/dist/napi/index.js +60 -0
- package/embedded.d.ts +1 -0
- package/embedded.js +46 -0
- package/package.json +20 -9
- package/dist/.tsbuildinfo +0 -1
- package/dist/better-sqlite3-shared.d.ts +0 -12
- package/dist/better-sqlite3-shared.d.ts.map +0 -1
- package/dist/better-sqlite3-shared.js +0 -143
- package/dist/better-sqlite3-shared.js.map +0 -1
- package/dist/better-sqlite3.d.ts +0 -4
- package/dist/better-sqlite3.d.ts.map +0 -1
- package/dist/better-sqlite3.js +0 -8
- package/dist/better-sqlite3.js.map +0 -1
- package/dist/cli.d.ts +0 -7
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/local-first/unified-api.d.ts +0 -110
- package/dist/local-first/unified-api.d.ts.map +0 -1
- package/dist/local-first/unified-api.js +0 -348
- package/dist/local-first/unified-api.js.map +0 -1
- package/dist/node-index.d.ts +0 -150
- package/dist/node-index.d.ts.map +0 -1
- package/dist/node-index.js +0 -668
- package/dist/node-index.js.map +0 -1
- package/dist/node-wrapper.d.ts +0 -44
- package/dist/node-wrapper.d.ts.map +0 -1
- package/dist/node-wrapper.js +0 -296
- package/dist/node-wrapper.js.map +0 -1
- package/dist/types/index.d.ts +0 -28
- package/dist/types/index.d.ts.map +0 -1
- package/dist/types/index.js +0 -3
- package/dist/types/index.js.map +0 -1
- package/dist/types/node-types.d.ts +0 -71
- package/dist/types/node-types.d.ts.map +0 -1
- package/dist/types/node-types.js +0 -6
- package/dist/types/node-types.js.map +0 -1
- package/dist/util/debug.d.ts +0 -3
- package/dist/util/debug.d.ts.map +0 -1
- package/dist/util/debug.js +0 -34
- package/dist/util/debug.js.map +0 -1
- package/dist/vscode/extension.d.ts +0 -81
- package/dist/vscode/extension.d.ts.map +0 -1
- package/dist/vscode/extension.js +0 -309
- package/dist/vscode/extension.js.map +0 -1
- package/examples/basic-usage.d.ts +0 -2
- package/examples/basic-usage.d.ts.map +0 -1
- package/examples/basic-usage.js +0 -26
- package/examples/basic-usage.js.map +0 -1
- package/examples/basic-usage.ts +0 -29
- package/examples/browser-demo/README.md +0 -204
- package/examples/browser-demo/index.html +0 -466
- package/examples/browser-wasm-integration.md +0 -411
- package/examples/ipc-demo/README.md +0 -127
- package/examples/local-first-usage.ts +0 -138
- package/examples/native-ipc-integration.md +0 -526
- package/examples/tauri-demo/README.md +0 -240
- package/examples/tauri-integration.md +0 -260
- package/examples/vscode-extension-example/README.md +0 -95
- package/examples/vscode-extension-example/package.json +0 -49
- package/examples/vscode-extension-example/src/extension.ts +0 -172
- package/examples/vscode-extension-example/tsconfig.json +0 -12
- package/examples/vscode-extension-integration.d.ts +0 -31
- package/examples/vscode-extension-integration.d.ts.map +0 -1
- package/examples/vscode-extension-integration.js +0 -319
- package/examples/vscode-extension-integration.js.map +0 -1
- package/examples/vscode-extension-integration.ts +0 -41
- package/legacy/benchmarks/memory-benchmarks.ts +0 -350
- package/legacy/benchmarks/run-benchmarks.ts +0 -315
- package/legacy/better-sqlite3-shared.ts +0 -157
- package/legacy/better-sqlite3.ts +0 -4
- package/legacy/cli.ts +0 -241
- package/legacy/config.ts +0 -50
- package/legacy/core/crdt.ts +0 -107
- package/legacy/core/database.ts +0 -529
- package/legacy/healthcheck.ts +0 -162
- package/legacy/http/api-server.ts +0 -569
- package/legacy/index.ts +0 -31
- package/legacy/local-first/unified-api.ts +0 -449
- package/legacy/logic/rules.ts +0 -46
- package/legacy/main.rs +0 -3
- package/legacy/main.ts +0 -197
- package/legacy/network/websocket-server.ts +0 -115
- package/legacy/node-index.ts +0 -827
- package/legacy/node-wrapper.ts +0 -329
- package/legacy/plugins/README.md +0 -181
- package/legacy/plugins/example-embedding-plugin.ts +0 -56
- package/legacy/plugins/plugin-system.ts +0 -315
- package/legacy/sqlite-compat.ts +0 -633
- package/legacy/sqlite3-compat.ts +0 -55
- package/legacy/storage/kv-storage.ts +0 -73
- package/legacy/tests/core.test.ts +0 -305
- package/legacy/tests/fixtures/performance-data.json +0 -71
- package/legacy/tests/fixtures/test-data.json +0 -129
- package/legacy/tests/integration/api-server.test.ts +0 -334
- package/legacy/tests/integration/mesh-network.test.ts +0 -303
- package/legacy/tests/logic.test.ts +0 -34
- package/legacy/tests/performance/load.test.ts +0 -290
- package/legacy/tests/security/input-validation.test.ts +0 -286
- package/legacy/tests/unit/core.test.ts +0 -226
- package/legacy/tests/unit/local-first-api.test.ts +0 -65
- package/legacy/tests/unit/plugin-system.test.ts +0 -388
- package/legacy/tests/unit/subscriptions.test.ts +0 -135
- package/legacy/tests/unit/vector-search.test.ts +0 -173
- package/legacy/tests/vscode_extension_test.ts +0 -281
- package/legacy/types/index.ts +0 -32
- package/legacy/types/node-types.ts +0 -80
- package/legacy/util/debug.ts +0 -27
- package/legacy/vector/index.ts +0 -59
- package/legacy/vscode/extension.ts +0 -387
- package/scripts/compiled-crud-verify.ts +0 -30
- package/scripts/dogfood.ts +0 -297
- package/scripts/postinstall.js +0 -156
- package/scripts/publish-crates.sh +0 -95
- package/scripts/release-check.js +0 -224
- package/scripts/run-tests.ts +0 -178
- package/scripts/setup-libclang.ps1 +0 -209
- package/scripts/update-changelog.js +0 -214
- package/scripts/validate-npm-publish.js +0 -228
- package/web/README.md +0 -27
- package/web/svelte/package.json +0 -31
|
@@ -1,334 +0,0 @@
|
|
|
1
|
-
// @ts-nocheck
|
|
2
|
-
import { assertEquals, assertExists } from "jsr:@std/assert@1.0.14";
|
|
3
|
-
import { GunDB } from "../../core/database.ts";
|
|
4
|
-
import { type ApiServerHandle, startApiServer } from "../../http/api-server.ts";
|
|
5
|
-
|
|
6
|
-
function randomPort(): number {
|
|
7
|
-
return 18000 + Math.floor(Math.random() * 10000);
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
Deno.test("API Server - HTTP Endpoints", async () => {
|
|
11
|
-
const db = new GunDB();
|
|
12
|
-
let api: ApiServerHandle | null = null;
|
|
13
|
-
try {
|
|
14
|
-
const kvPath = await Deno.makeTempFile({
|
|
15
|
-
prefix: "kv_",
|
|
16
|
-
suffix: ".sqlite",
|
|
17
|
-
});
|
|
18
|
-
await db.ready(kvPath);
|
|
19
|
-
|
|
20
|
-
const port = randomPort();
|
|
21
|
-
const apiPort = port + 1;
|
|
22
|
-
db.serve({ port });
|
|
23
|
-
api = startApiServer({ port: apiPort, db });
|
|
24
|
-
|
|
25
|
-
const baseUrl = `http://localhost:${apiPort}`;
|
|
26
|
-
|
|
27
|
-
// Test PUT endpoint
|
|
28
|
-
const putResponse = await fetch(`${baseUrl}/api/nodes/test:1`, {
|
|
29
|
-
method: "PUT",
|
|
30
|
-
headers: { "Content-Type": "application/json" },
|
|
31
|
-
body: JSON.stringify({ name: "Test Node", value: 123 }),
|
|
32
|
-
});
|
|
33
|
-
assertEquals(putResponse.status, 200);
|
|
34
|
-
await putResponse.body?.cancel();
|
|
35
|
-
|
|
36
|
-
// Test GET endpoint
|
|
37
|
-
const getResponse = await fetch(`${baseUrl}/api/nodes/test:1`);
|
|
38
|
-
assertEquals(getResponse.status, 200);
|
|
39
|
-
const data = await getResponse.json();
|
|
40
|
-
assertEquals(data.name, "Test Node");
|
|
41
|
-
assertEquals(data.value, 123);
|
|
42
|
-
|
|
43
|
-
// Test DELETE endpoint
|
|
44
|
-
const deleteResponse = await fetch(`${baseUrl}/api/nodes/test:1`, {
|
|
45
|
-
method: "DELETE",
|
|
46
|
-
});
|
|
47
|
-
assertEquals(deleteResponse.status, 200);
|
|
48
|
-
await deleteResponse.body?.cancel();
|
|
49
|
-
|
|
50
|
-
// Verify deletion
|
|
51
|
-
const getAfterDelete = await fetch(`${baseUrl}/api/nodes/test:1`);
|
|
52
|
-
assertEquals(getAfterDelete.status, 404);
|
|
53
|
-
await getAfterDelete.body?.cancel();
|
|
54
|
-
} finally {
|
|
55
|
-
api?.close();
|
|
56
|
-
await db.close();
|
|
57
|
-
}
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
Deno.test("API Server - Vector Search Endpoint", async () => {
|
|
61
|
-
const db = new GunDB();
|
|
62
|
-
let api: ApiServerHandle | null = null;
|
|
63
|
-
try {
|
|
64
|
-
const kvPath = await Deno.makeTempFile({
|
|
65
|
-
prefix: "kv_",
|
|
66
|
-
suffix: ".sqlite",
|
|
67
|
-
});
|
|
68
|
-
await db.ready(kvPath);
|
|
69
|
-
|
|
70
|
-
// Add test data
|
|
71
|
-
await db.put("doc:1", { text: "Machine learning algorithms" });
|
|
72
|
-
await db.put("doc:2", { text: "Cooking recipes and food" });
|
|
73
|
-
|
|
74
|
-
const port = randomPort();
|
|
75
|
-
const apiPort = port + 1;
|
|
76
|
-
db.serve({ port });
|
|
77
|
-
api = startApiServer({ port: apiPort, db });
|
|
78
|
-
|
|
79
|
-
const baseUrl = `http://localhost:${apiPort}`;
|
|
80
|
-
|
|
81
|
-
// Test vector search endpoint
|
|
82
|
-
const searchResponse = await fetch(`${baseUrl}/api/search`, {
|
|
83
|
-
method: "POST",
|
|
84
|
-
headers: { "Content-Type": "application/json" },
|
|
85
|
-
body: JSON.stringify({
|
|
86
|
-
query: "machine learning",
|
|
87
|
-
limit: 5,
|
|
88
|
-
}),
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
assertEquals(searchResponse.status, 200);
|
|
92
|
-
const results = await searchResponse.json();
|
|
93
|
-
assertExists(results);
|
|
94
|
-
assertEquals(Array.isArray(results), true);
|
|
95
|
-
} finally {
|
|
96
|
-
api?.close();
|
|
97
|
-
await db.close();
|
|
98
|
-
}
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
Deno.test("API Server - WebSocket Connection", async () => {
|
|
102
|
-
const db = new GunDB();
|
|
103
|
-
let api: ApiServerHandle | null = null;
|
|
104
|
-
try {
|
|
105
|
-
const kvPath = await Deno.makeTempFile({
|
|
106
|
-
prefix: "kv_",
|
|
107
|
-
suffix: ".sqlite",
|
|
108
|
-
});
|
|
109
|
-
await db.ready(kvPath);
|
|
110
|
-
|
|
111
|
-
const port = randomPort();
|
|
112
|
-
const apiPort = port + 1;
|
|
113
|
-
db.serve({ port });
|
|
114
|
-
api = startApiServer({ port: apiPort, db });
|
|
115
|
-
|
|
116
|
-
const wsUrl = `ws://localhost:${port}/ws`;
|
|
117
|
-
|
|
118
|
-
// Test WebSocket connection
|
|
119
|
-
const ws = new WebSocket(wsUrl);
|
|
120
|
-
|
|
121
|
-
const connectionPromise = new Promise((resolve, reject) => {
|
|
122
|
-
const timer = setTimeout(
|
|
123
|
-
() => reject(new Error("Connection timeout")),
|
|
124
|
-
5000,
|
|
125
|
-
);
|
|
126
|
-
ws.onopen = () => {
|
|
127
|
-
clearTimeout(timer);
|
|
128
|
-
resolve(true);
|
|
129
|
-
};
|
|
130
|
-
ws.onerror = (error) => {
|
|
131
|
-
clearTimeout(timer);
|
|
132
|
-
reject(error);
|
|
133
|
-
};
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
await connectionPromise;
|
|
137
|
-
|
|
138
|
-
// Test sending data via WebSocket
|
|
139
|
-
const messagePromise = new Promise((resolve) => {
|
|
140
|
-
ws.onmessage = (event) => {
|
|
141
|
-
const data = JSON.parse(event.data);
|
|
142
|
-
if (data.type === "put") {
|
|
143
|
-
resolve(data);
|
|
144
|
-
}
|
|
145
|
-
};
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
// Send a message
|
|
149
|
-
ws.send(
|
|
150
|
-
JSON.stringify({
|
|
151
|
-
type: "put",
|
|
152
|
-
id: "ws:test",
|
|
153
|
-
data: { message: "Hello WebSocket" },
|
|
154
|
-
}),
|
|
155
|
-
);
|
|
156
|
-
|
|
157
|
-
await messagePromise;
|
|
158
|
-
ws.close();
|
|
159
|
-
} finally {
|
|
160
|
-
api?.close();
|
|
161
|
-
await db.close();
|
|
162
|
-
}
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
Deno.test("API Server - Error Handling", async () => {
|
|
166
|
-
const db = new GunDB();
|
|
167
|
-
let api: ApiServerHandle | null = null;
|
|
168
|
-
try {
|
|
169
|
-
const kvPath = await Deno.makeTempFile({
|
|
170
|
-
prefix: "kv_",
|
|
171
|
-
suffix: ".sqlite",
|
|
172
|
-
});
|
|
173
|
-
await db.ready(kvPath);
|
|
174
|
-
|
|
175
|
-
const port = randomPort();
|
|
176
|
-
const apiPort = port + 1;
|
|
177
|
-
db.serve({ port });
|
|
178
|
-
api = startApiServer({ port: apiPort, db });
|
|
179
|
-
|
|
180
|
-
const baseUrl = `http://localhost:${apiPort}`;
|
|
181
|
-
|
|
182
|
-
// Test 404 for non-existent node
|
|
183
|
-
const notFoundResponse = await fetch(`${baseUrl}/api/nodes/nonexistent`);
|
|
184
|
-
assertEquals(notFoundResponse.status, 404);
|
|
185
|
-
await notFoundResponse.body?.cancel();
|
|
186
|
-
|
|
187
|
-
// Test 400 for invalid JSON
|
|
188
|
-
const invalidJsonResponse = await fetch(`${baseUrl}/api/nodes/test`, {
|
|
189
|
-
method: "PUT",
|
|
190
|
-
headers: { "Content-Type": "application/json" },
|
|
191
|
-
body: "invalid json",
|
|
192
|
-
});
|
|
193
|
-
assertEquals(invalidJsonResponse.status, 400);
|
|
194
|
-
await invalidJsonResponse.body?.cancel();
|
|
195
|
-
} finally {
|
|
196
|
-
api?.close();
|
|
197
|
-
await db.close();
|
|
198
|
-
}
|
|
199
|
-
});
|
|
200
|
-
|
|
201
|
-
Deno.test("API Server - CORS Headers", async () => {
|
|
202
|
-
const db = new GunDB();
|
|
203
|
-
let api: ApiServerHandle | null = null;
|
|
204
|
-
try {
|
|
205
|
-
const kvPath = await Deno.makeTempFile({
|
|
206
|
-
prefix: "kv_",
|
|
207
|
-
suffix: ".sqlite",
|
|
208
|
-
});
|
|
209
|
-
await db.ready(kvPath);
|
|
210
|
-
|
|
211
|
-
const port = randomPort();
|
|
212
|
-
const apiPort = port + 1;
|
|
213
|
-
db.serve({ port });
|
|
214
|
-
api = startApiServer({ port: apiPort, db });
|
|
215
|
-
|
|
216
|
-
const baseUrl = `http://localhost:${apiPort}`;
|
|
217
|
-
|
|
218
|
-
// Test OPTIONS request for CORS
|
|
219
|
-
const optionsResponse = await fetch(`${baseUrl}/api/nodes/test`, {
|
|
220
|
-
method: "OPTIONS",
|
|
221
|
-
headers: {
|
|
222
|
-
Origin: "http://localhost:3000",
|
|
223
|
-
"Access-Control-Request-Method": "PUT",
|
|
224
|
-
},
|
|
225
|
-
});
|
|
226
|
-
|
|
227
|
-
assertEquals(optionsResponse.status, 200);
|
|
228
|
-
assertExists(optionsResponse.headers.get("Access-Control-Allow-Origin"));
|
|
229
|
-
assertExists(optionsResponse.headers.get("Access-Control-Allow-Methods"));
|
|
230
|
-
await optionsResponse.body?.cancel();
|
|
231
|
-
} finally {
|
|
232
|
-
api?.close();
|
|
233
|
-
await db.close();
|
|
234
|
-
}
|
|
235
|
-
});
|
|
236
|
-
|
|
237
|
-
Deno.test("API Server - P2P API Endpoints", async () => {
|
|
238
|
-
const db = new GunDB();
|
|
239
|
-
let api: ApiServerHandle | null = null;
|
|
240
|
-
try {
|
|
241
|
-
const kvPath = await Deno.makeTempFile({
|
|
242
|
-
prefix: "kv_",
|
|
243
|
-
suffix: ".sqlite",
|
|
244
|
-
});
|
|
245
|
-
await db.ready(kvPath);
|
|
246
|
-
|
|
247
|
-
const port = randomPort();
|
|
248
|
-
const apiPort = port + 1;
|
|
249
|
-
db.serve({ port });
|
|
250
|
-
api = startApiServer({ port: apiPort, db });
|
|
251
|
-
|
|
252
|
-
const baseUrl = `http://localhost:${apiPort}`;
|
|
253
|
-
|
|
254
|
-
// Test createIdentity endpoint
|
|
255
|
-
const identityResponse = await fetch(`${baseUrl}/api/identity`, {
|
|
256
|
-
method: "POST",
|
|
257
|
-
headers: { "Content-Type": "application/json" },
|
|
258
|
-
body: JSON.stringify({ name: "John Doe", email: "john@example.com" }),
|
|
259
|
-
});
|
|
260
|
-
assertEquals(identityResponse.status, 200);
|
|
261
|
-
const identity = await identityResponse.json();
|
|
262
|
-
assertExists(identity.id);
|
|
263
|
-
assertExists(identity.publicKey);
|
|
264
|
-
assertEquals(identity.name, "John Doe");
|
|
265
|
-
assertEquals(identity.email, "john@example.com");
|
|
266
|
-
|
|
267
|
-
// Test searchPeers endpoint
|
|
268
|
-
const peersResponse = await fetch(`${baseUrl}/api/peers/search?q=developer`);
|
|
269
|
-
assertEquals(peersResponse.status, 200);
|
|
270
|
-
const peers = await peersResponse.json();
|
|
271
|
-
assertEquals(Array.isArray(peers), true);
|
|
272
|
-
|
|
273
|
-
// Test shareNode endpoint
|
|
274
|
-
const shareResponse = await fetch(`${baseUrl}/api/share`, {
|
|
275
|
-
method: "POST",
|
|
276
|
-
headers: { "Content-Type": "application/json" },
|
|
277
|
-
body: JSON.stringify({
|
|
278
|
-
nodeId: "node:123",
|
|
279
|
-
targetPeerId: "peer:456",
|
|
280
|
-
accessLevel: "read-only",
|
|
281
|
-
}),
|
|
282
|
-
});
|
|
283
|
-
assertEquals(shareResponse.status, 200);
|
|
284
|
-
const shareData = await shareResponse.json();
|
|
285
|
-
assertExists(shareData.sharedNodeId);
|
|
286
|
-
assertEquals(shareData.nodeId, "node:123");
|
|
287
|
-
assertEquals(shareData.targetPeerId, "peer:456");
|
|
288
|
-
assertEquals(shareData.accessLevel, "read-only");
|
|
289
|
-
|
|
290
|
-
// Test acceptSharedNode endpoint
|
|
291
|
-
const acceptResponse = await fetch(`${baseUrl}/api/share/accept`, {
|
|
292
|
-
method: "POST",
|
|
293
|
-
headers: { "Content-Type": "application/json" },
|
|
294
|
-
body: JSON.stringify({ sharedNodeId: "shared:789" }),
|
|
295
|
-
});
|
|
296
|
-
assertEquals(acceptResponse.status, 200);
|
|
297
|
-
const acceptData = await acceptResponse.json();
|
|
298
|
-
assertEquals(acceptData.success, true);
|
|
299
|
-
assertEquals(acceptData.sharedNodeId, "shared:789");
|
|
300
|
-
|
|
301
|
-
// Test addDevice endpoint
|
|
302
|
-
const deviceResponse = await fetch(`${baseUrl}/api/devices`, {
|
|
303
|
-
method: "POST",
|
|
304
|
-
headers: { "Content-Type": "application/json" },
|
|
305
|
-
body: JSON.stringify({ name: "My Laptop", type: "laptop" }),
|
|
306
|
-
});
|
|
307
|
-
assertEquals(deviceResponse.status, 200);
|
|
308
|
-
const device = await deviceResponse.json();
|
|
309
|
-
assertExists(device.id);
|
|
310
|
-
assertEquals(device.name, "My Laptop");
|
|
311
|
-
assertEquals(device.type, "laptop");
|
|
312
|
-
assertEquals(device.status, "online");
|
|
313
|
-
|
|
314
|
-
// Test syncWithDevice endpoint
|
|
315
|
-
const syncResponse = await fetch(`${baseUrl}/api/devices/sync`, {
|
|
316
|
-
method: "POST",
|
|
317
|
-
headers: { "Content-Type": "application/json" },
|
|
318
|
-
body: JSON.stringify({ deviceId: "device:123" }),
|
|
319
|
-
});
|
|
320
|
-
assertEquals(syncResponse.status, 200);
|
|
321
|
-
const syncData = await syncResponse.json();
|
|
322
|
-
assertEquals(syncData.success, true);
|
|
323
|
-
assertEquals(syncData.deviceId, "device:123");
|
|
324
|
-
|
|
325
|
-
// Test GET devices list
|
|
326
|
-
const devicesListResponse = await fetch(`${baseUrl}/api/devices`);
|
|
327
|
-
assertEquals(devicesListResponse.status, 200);
|
|
328
|
-
const devicesList = await devicesListResponse.json();
|
|
329
|
-
assertEquals(Array.isArray(devicesList), true);
|
|
330
|
-
} finally {
|
|
331
|
-
api?.close();
|
|
332
|
-
await db.close();
|
|
333
|
-
}
|
|
334
|
-
});
|
|
@@ -1,303 +0,0 @@
|
|
|
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(
|
|
88
|
-
receivedData as Promise<unknown>,
|
|
89
|
-
"Timed out waiting for initial mesh sync",
|
|
90
|
-
);
|
|
91
|
-
|
|
92
|
-
// Verify data was received
|
|
93
|
-
const syncedData = await dbB.get("mesh:test");
|
|
94
|
-
assertExists(syncedData);
|
|
95
|
-
assertEquals((syncedData as any).text, "Hello from server A");
|
|
96
|
-
} finally {
|
|
97
|
-
await dbB.close();
|
|
98
|
-
await dbA.close();
|
|
99
|
-
}
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
meshTest("Mesh Network - Bidirectional Sync", async () => {
|
|
103
|
-
const port = randomPort();
|
|
104
|
-
const serverUrl = `ws://localhost:${port}`;
|
|
105
|
-
|
|
106
|
-
const dbA = new GunDB();
|
|
107
|
-
const dbB = new GunDB();
|
|
108
|
-
|
|
109
|
-
try {
|
|
110
|
-
const kvA = await Deno.makeTempFile({ prefix: "kv_", suffix: ".sqlite" });
|
|
111
|
-
const kvB = await Deno.makeTempFile({ prefix: "kv_", suffix: ".sqlite" });
|
|
112
|
-
|
|
113
|
-
await dbA.ready(kvA);
|
|
114
|
-
await dbB.ready(kvB);
|
|
115
|
-
await dbA.serve({ port });
|
|
116
|
-
|
|
117
|
-
// Connect B to A
|
|
118
|
-
dbB.connect(serverUrl);
|
|
119
|
-
await withTimeout(
|
|
120
|
-
new Promise((resolve) => setTimeout(resolve, 100)),
|
|
121
|
-
"Timed out waiting for mesh connection",
|
|
122
|
-
);
|
|
123
|
-
|
|
124
|
-
// A sends data to B
|
|
125
|
-
await dbA.put("from:A", { message: "Hello from A" });
|
|
126
|
-
|
|
127
|
-
const receivedFromA = new Promise((resolve) => {
|
|
128
|
-
dbB.on("from:A", (node) => {
|
|
129
|
-
if (node && (node.data as any).message === "Hello from A") {
|
|
130
|
-
resolve(true);
|
|
131
|
-
}
|
|
132
|
-
});
|
|
133
|
-
});
|
|
134
|
-
await withTimeout(
|
|
135
|
-
receivedFromA as Promise<unknown>,
|
|
136
|
-
"Timed out waiting for data from node A",
|
|
137
|
-
);
|
|
138
|
-
|
|
139
|
-
// B sends data to A
|
|
140
|
-
await dbB.put("from:B", { message: "Hello from B" });
|
|
141
|
-
|
|
142
|
-
const receivedFromB = new Promise((resolve) => {
|
|
143
|
-
dbA.on("from:B", (node) => {
|
|
144
|
-
if (node && (node.data as any).message === "Hello from B") {
|
|
145
|
-
resolve(true);
|
|
146
|
-
}
|
|
147
|
-
});
|
|
148
|
-
});
|
|
149
|
-
await withTimeout(
|
|
150
|
-
receivedFromB as Promise<unknown>,
|
|
151
|
-
"Timed out waiting for data from node B",
|
|
152
|
-
);
|
|
153
|
-
|
|
154
|
-
// Verify both received data
|
|
155
|
-
const dataFromA = await dbB.get("from:A");
|
|
156
|
-
const dataFromB = await dbA.get("from:B");
|
|
157
|
-
|
|
158
|
-
assertExists(dataFromA);
|
|
159
|
-
assertExists(dataFromB);
|
|
160
|
-
assertEquals((dataFromA as any).message, "Hello from A");
|
|
161
|
-
assertEquals((dataFromB as any).message, "Hello from B");
|
|
162
|
-
} finally {
|
|
163
|
-
await dbB.close();
|
|
164
|
-
await dbA.close();
|
|
165
|
-
}
|
|
166
|
-
});
|
|
167
|
-
|
|
168
|
-
meshTest("Mesh Network - Conflict Resolution", async () => {
|
|
169
|
-
const port = randomPort();
|
|
170
|
-
const serverUrl = `ws://localhost:${port}`;
|
|
171
|
-
|
|
172
|
-
const dbA = new GunDB();
|
|
173
|
-
const dbB = new GunDB();
|
|
174
|
-
|
|
175
|
-
try {
|
|
176
|
-
const kvA = await Deno.makeTempFile({ prefix: "kv_", suffix: ".sqlite" });
|
|
177
|
-
const kvB = await Deno.makeTempFile({ prefix: "kv_", suffix: ".sqlite" });
|
|
178
|
-
|
|
179
|
-
await dbA.ready(kvA);
|
|
180
|
-
await dbB.ready(kvB);
|
|
181
|
-
await dbA.serve({ port });
|
|
182
|
-
|
|
183
|
-
// Connect B to A
|
|
184
|
-
dbB.connect(serverUrl);
|
|
185
|
-
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
186
|
-
|
|
187
|
-
// Both modify the same key simultaneously
|
|
188
|
-
const timestamp = Date.now();
|
|
189
|
-
await dbA.put("conflict:test", {
|
|
190
|
-
value: "from A",
|
|
191
|
-
timestamp: timestamp + 1000,
|
|
192
|
-
});
|
|
193
|
-
await dbB.put("conflict:test", {
|
|
194
|
-
value: "from B",
|
|
195
|
-
timestamp: timestamp + 2000,
|
|
196
|
-
});
|
|
197
|
-
|
|
198
|
-
// Wait for sync
|
|
199
|
-
await withTimeout(
|
|
200
|
-
new Promise((resolve) => setTimeout(resolve, 500)),
|
|
201
|
-
"Timed out waiting for conflict resolution",
|
|
202
|
-
);
|
|
203
|
-
|
|
204
|
-
// Check that both databases have the same final state
|
|
205
|
-
const finalA = await dbA.get("conflict:test");
|
|
206
|
-
const finalB = await dbB.get("conflict:test");
|
|
207
|
-
|
|
208
|
-
assertExists(finalA);
|
|
209
|
-
assertExists(finalB);
|
|
210
|
-
|
|
211
|
-
// Should have the newer timestamp (from B)
|
|
212
|
-
assertEquals((finalA as any).value, "from B");
|
|
213
|
-
assertEquals((finalB as any).value, "from B");
|
|
214
|
-
} finally {
|
|
215
|
-
await dbB.close();
|
|
216
|
-
await dbA.close();
|
|
217
|
-
}
|
|
218
|
-
});
|
|
219
|
-
|
|
220
|
-
meshTest("Mesh Network - Multiple Clients", async () => {
|
|
221
|
-
const port = randomPort();
|
|
222
|
-
const serverUrl = `ws://localhost:${port}`;
|
|
223
|
-
|
|
224
|
-
const dbA = new GunDB(); // Server
|
|
225
|
-
const dbB = new GunDB(); // Client 1
|
|
226
|
-
const dbC = new GunDB(); // Client 2
|
|
227
|
-
|
|
228
|
-
try {
|
|
229
|
-
const kvA = await Deno.makeTempFile({ prefix: "kv_", suffix: ".sqlite" });
|
|
230
|
-
const kvB = await Deno.makeTempFile({ prefix: "kv_", suffix: ".sqlite" });
|
|
231
|
-
const kvC = await Deno.makeTempFile({ prefix: "kv_", suffix: ".sqlite" });
|
|
232
|
-
|
|
233
|
-
await dbA.ready(kvA);
|
|
234
|
-
await dbB.ready(kvB);
|
|
235
|
-
await dbC.ready(kvC);
|
|
236
|
-
await dbA.serve({ port });
|
|
237
|
-
|
|
238
|
-
// Connect both clients
|
|
239
|
-
dbB.connect(serverUrl);
|
|
240
|
-
dbC.connect(serverUrl);
|
|
241
|
-
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
242
|
-
|
|
243
|
-
// B sends data
|
|
244
|
-
await dbB.put("multi:test", { from: "B", message: "Hello from B" });
|
|
245
|
-
|
|
246
|
-
// Wait for both A and C to receive
|
|
247
|
-
const receivedOnA = new Promise((resolve) => {
|
|
248
|
-
dbA.on("multi:test", (node) => {
|
|
249
|
-
if (node && (node.data as any).from === "B") resolve(true);
|
|
250
|
-
});
|
|
251
|
-
});
|
|
252
|
-
|
|
253
|
-
const receivedOnC = new Promise((resolve) => {
|
|
254
|
-
dbC.on("multi:test", (node) => {
|
|
255
|
-
if (node && (node.data as any).from === "B") resolve(true);
|
|
256
|
-
});
|
|
257
|
-
});
|
|
258
|
-
|
|
259
|
-
await withTimeout(
|
|
260
|
-
Promise.all([receivedOnA, receivedOnC]),
|
|
261
|
-
"Timed out waiting for multi-client propagation",
|
|
262
|
-
);
|
|
263
|
-
|
|
264
|
-
// Verify all have the data
|
|
265
|
-
const dataA = await dbA.get("multi:test");
|
|
266
|
-
const dataB = await dbB.get("multi:test");
|
|
267
|
-
const dataC = await dbC.get("multi:test");
|
|
268
|
-
|
|
269
|
-
assertExists(dataA);
|
|
270
|
-
assertExists(dataB);
|
|
271
|
-
assertExists(dataC);
|
|
272
|
-
assertEquals((dataA as any).from, "B");
|
|
273
|
-
assertEquals((dataB as any).from, "B");
|
|
274
|
-
assertEquals((dataC as any).from, "B");
|
|
275
|
-
} finally {
|
|
276
|
-
await dbC.close();
|
|
277
|
-
await dbB.close();
|
|
278
|
-
await dbA.close();
|
|
279
|
-
}
|
|
280
|
-
});
|
|
281
|
-
|
|
282
|
-
meshTest("Mesh Network - Connection Error Handling", async () => {
|
|
283
|
-
const db = new GunDB();
|
|
284
|
-
|
|
285
|
-
try {
|
|
286
|
-
const kvPath = await Deno.makeTempFile({
|
|
287
|
-
prefix: "kv_",
|
|
288
|
-
suffix: ".sqlite",
|
|
289
|
-
});
|
|
290
|
-
await db.ready(kvPath);
|
|
291
|
-
|
|
292
|
-
// Try to connect to non-existent server
|
|
293
|
-
db.connect("ws://localhost:99999");
|
|
294
|
-
|
|
295
|
-
// Should not throw error, but connection should fail gracefully
|
|
296
|
-
await withTimeout(
|
|
297
|
-
new Promise((resolve) => setTimeout(resolve, 1000)),
|
|
298
|
-
"Timed out waiting for graceful connection failure",
|
|
299
|
-
);
|
|
300
|
-
} finally {
|
|
301
|
-
await db.close();
|
|
302
|
-
}
|
|
303
|
-
});
|
|
@@ -1,34 +0,0 @@
|
|
|
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({
|
|
8
|
-
prefix: "kv_",
|
|
9
|
-
suffix: ".sqlite",
|
|
10
|
-
});
|
|
11
|
-
await db.ready(kvPath);
|
|
12
|
-
|
|
13
|
-
const rule: Rule = {
|
|
14
|
-
name: "adultClassifier",
|
|
15
|
-
whenType: "Person",
|
|
16
|
-
predicate: (node) =>
|
|
17
|
-
typeof (node.data as any).age === "number" &&
|
|
18
|
-
(node.data as any).age >= 18,
|
|
19
|
-
action: async (ctx, node) => {
|
|
20
|
-
const data = { ...(node.data as Record<string, unknown>), adult: true };
|
|
21
|
-
await ctx.db.put(node.id, data);
|
|
22
|
-
},
|
|
23
|
-
};
|
|
24
|
-
db.addRule(rule);
|
|
25
|
-
|
|
26
|
-
await db.put("p:alice", { name: "Alice", age: 20, type: "Person" });
|
|
27
|
-
const got = await db.get<{ adult?: boolean }>("p:alice");
|
|
28
|
-
if (!got || got.adult !== true) {
|
|
29
|
-
throw new Error("Expected adult flag set by rule");
|
|
30
|
-
}
|
|
31
|
-
} finally {
|
|
32
|
-
await db.close();
|
|
33
|
-
}
|
|
34
|
-
});
|