@plures/pluresdb 1.5.3 → 1.6.10

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.
@@ -0,0 +1,466 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>PluresDB Browser Demo - Local-First Integration</title>
7
+ <style>
8
+ * {
9
+ margin: 0;
10
+ padding: 0;
11
+ box-sizing: border-box;
12
+ }
13
+
14
+ body {
15
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
16
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
17
+ padding: 20px;
18
+ min-height: 100vh;
19
+ }
20
+
21
+ .container {
22
+ max-width: 900px;
23
+ margin: 0 auto;
24
+ background: white;
25
+ border-radius: 12px;
26
+ box-shadow: 0 10px 40px rgba(0, 0, 0, 0.2);
27
+ overflow: hidden;
28
+ }
29
+
30
+ header {
31
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
32
+ color: white;
33
+ padding: 30px;
34
+ text-align: center;
35
+ }
36
+
37
+ header h1 {
38
+ font-size: 2.5em;
39
+ margin-bottom: 10px;
40
+ }
41
+
42
+ header p {
43
+ font-size: 1.1em;
44
+ opacity: 0.9;
45
+ }
46
+
47
+ .status {
48
+ background: #f0f4f8;
49
+ padding: 15px 30px;
50
+ border-bottom: 2px solid #e2e8f0;
51
+ }
52
+
53
+ .status-badge {
54
+ display: inline-block;
55
+ padding: 6px 12px;
56
+ border-radius: 20px;
57
+ font-size: 0.9em;
58
+ font-weight: 600;
59
+ }
60
+
61
+ .status-badge.active {
62
+ background: #48bb78;
63
+ color: white;
64
+ }
65
+
66
+ .status-badge.fallback {
67
+ background: #ed8936;
68
+ color: white;
69
+ }
70
+
71
+ main {
72
+ padding: 30px;
73
+ }
74
+
75
+ .section {
76
+ margin-bottom: 30px;
77
+ }
78
+
79
+ .section h2 {
80
+ color: #2d3748;
81
+ margin-bottom: 15px;
82
+ font-size: 1.5em;
83
+ }
84
+
85
+ .form-group {
86
+ margin-bottom: 15px;
87
+ }
88
+
89
+ label {
90
+ display: block;
91
+ margin-bottom: 5px;
92
+ color: #4a5568;
93
+ font-weight: 600;
94
+ }
95
+
96
+ input {
97
+ width: 100%;
98
+ padding: 12px;
99
+ border: 2px solid #e2e8f0;
100
+ border-radius: 6px;
101
+ font-size: 1em;
102
+ transition: border-color 0.3s;
103
+ }
104
+
105
+ input:focus {
106
+ outline: none;
107
+ border-color: #667eea;
108
+ }
109
+
110
+ button {
111
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
112
+ color: white;
113
+ border: none;
114
+ padding: 12px 24px;
115
+ border-radius: 6px;
116
+ font-size: 1em;
117
+ font-weight: 600;
118
+ cursor: pointer;
119
+ transition: transform 0.2s, box-shadow 0.2s;
120
+ }
121
+
122
+ button:hover {
123
+ transform: translateY(-2px);
124
+ box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);
125
+ }
126
+
127
+ button:active {
128
+ transform: translateY(0);
129
+ }
130
+
131
+ .button-group {
132
+ display: flex;
133
+ gap: 10px;
134
+ flex-wrap: wrap;
135
+ }
136
+
137
+ #output {
138
+ background: #2d3748;
139
+ color: #e2e8f0;
140
+ padding: 20px;
141
+ border-radius: 6px;
142
+ font-family: 'Courier New', monospace;
143
+ font-size: 0.9em;
144
+ max-height: 400px;
145
+ overflow-y: auto;
146
+ white-space: pre-wrap;
147
+ word-wrap: break-word;
148
+ }
149
+
150
+ .user-list {
151
+ list-style: none;
152
+ padding: 0;
153
+ }
154
+
155
+ .user-item {
156
+ background: #f7fafc;
157
+ padding: 15px;
158
+ margin-bottom: 10px;
159
+ border-radius: 6px;
160
+ border-left: 4px solid #667eea;
161
+ display: flex;
162
+ justify-content: space-between;
163
+ align-items: center;
164
+ }
165
+
166
+ .user-info {
167
+ flex: 1;
168
+ }
169
+
170
+ .user-name {
171
+ font-weight: 600;
172
+ color: #2d3748;
173
+ margin-bottom: 5px;
174
+ }
175
+
176
+ .user-email {
177
+ color: #718096;
178
+ font-size: 0.9em;
179
+ }
180
+
181
+ .delete-btn {
182
+ background: #fc8181;
183
+ padding: 8px 16px;
184
+ font-size: 0.9em;
185
+ }
186
+
187
+ .delete-btn:hover {
188
+ background: #f56565;
189
+ }
190
+
191
+ .info-box {
192
+ background: #ebf8ff;
193
+ border-left: 4px solid #4299e1;
194
+ padding: 15px;
195
+ border-radius: 6px;
196
+ margin-bottom: 20px;
197
+ }
198
+
199
+ .info-box strong {
200
+ color: #2c5282;
201
+ }
202
+ </style>
203
+ </head>
204
+ <body>
205
+ <div class="container">
206
+ <header>
207
+ <h1>🚀 PluresDB Browser Demo</h1>
208
+ <p>Local-First Database - Zero Network Overhead</p>
209
+ </header>
210
+
211
+ <div class="status">
212
+ <span>Integration Mode: </span>
213
+ <span id="mode-badge" class="status-badge fallback">Network (Fallback)</span>
214
+ <span style="margin-left: 15px; color: #718096;">
215
+ <span id="node-count">0</span> nodes stored
216
+ </span>
217
+ </div>
218
+
219
+ <main>
220
+ <div class="info-box">
221
+ <strong>ℹ️ Note:</strong> This demo currently runs in <strong>network fallback mode</strong> since WASM bindings are not yet fully implemented.
222
+ To use this demo, start the PluresDB server with <code>npm start</code>.
223
+ Once WASM is complete, this will run entirely in-browser with zero network overhead!
224
+ </div>
225
+
226
+ <!-- Add User Section -->
227
+ <div class="section">
228
+ <h2>📝 Add User</h2>
229
+ <div class="form-group">
230
+ <label for="userId">User ID</label>
231
+ <input type="text" id="userId" placeholder="e.g., user:alice" value="user:alice">
232
+ </div>
233
+ <div class="form-group">
234
+ <label for="userName">Name</label>
235
+ <input type="text" id="userName" placeholder="e.g., Alice Johnson" value="Alice Johnson">
236
+ </div>
237
+ <div class="form-group">
238
+ <label for="userEmail">Email</label>
239
+ <input type="email" id="userEmail" placeholder="e.g., alice@example.com" value="alice@example.com">
240
+ </div>
241
+ <button onclick="addUser()">➕ Add User</button>
242
+ </div>
243
+
244
+ <!-- Actions Section -->
245
+ <div class="section">
246
+ <h2>⚡ Actions</h2>
247
+ <div class="button-group">
248
+ <button onclick="listUsers()">📋 List All Users</button>
249
+ <button onclick="searchUsers()">🔍 Vector Search Demo</button>
250
+ <button onclick="clearOutput()">🗑️ Clear Output</button>
251
+ </div>
252
+ </div>
253
+
254
+ <!-- Users List -->
255
+ <div class="section" id="usersSection" style="display: none;">
256
+ <h2>👥 Users</h2>
257
+ <ul id="usersList" class="user-list"></ul>
258
+ </div>
259
+
260
+ <!-- Output Section -->
261
+ <div class="section">
262
+ <h2>📊 Output</h2>
263
+ <pre id="output">Ready. Click actions above to interact with the database.</pre>
264
+ </div>
265
+ </main>
266
+ </div>
267
+
268
+ <script type="module">
269
+ // Import PluresDB Local-First API
270
+ // Note: In production, this would use the actual module import
271
+ // For now, we'll use a mock implementation that demonstrates the network fallback
272
+
273
+ class MockPluresDBLocalFirst {
274
+ constructor() {
275
+ this.baseUrl = 'http://localhost:34567';
276
+ this.mode = 'network';
277
+ }
278
+
279
+ getMode() {
280
+ return this.mode;
281
+ }
282
+
283
+ async put(id, data) {
284
+ try {
285
+ const response = await fetch(`${this.baseUrl}/api/put`, {
286
+ method: 'POST',
287
+ headers: { 'Content-Type': 'application/json' },
288
+ body: JSON.stringify({ id, data })
289
+ });
290
+ if (!response.ok) throw new Error(`HTTP ${response.status}`);
291
+ return id;
292
+ } catch (error) {
293
+ throw new Error(`Failed to connect to PluresDB server. Please start it with 'npm start'`);
294
+ }
295
+ }
296
+
297
+ async get(id) {
298
+ const response = await fetch(`${this.baseUrl}/api/get?id=${encodeURIComponent(id)}`);
299
+ if (!response.ok && response.status !== 404) throw new Error(`HTTP ${response.status}`);
300
+ return response.status === 404 ? null : await response.json();
301
+ }
302
+
303
+ async list() {
304
+ const response = await fetch(`${this.baseUrl}/api/list`);
305
+ if (!response.ok) throw new Error(`HTTP ${response.status}`);
306
+ return await response.json();
307
+ }
308
+
309
+ async delete(id) {
310
+ const response = await fetch(`${this.baseUrl}/api/delete?id=${encodeURIComponent(id)}`, {
311
+ method: 'DELETE'
312
+ });
313
+ if (!response.ok) throw new Error(`HTTP ${response.status}`);
314
+ }
315
+
316
+ async vectorSearch(query, limit) {
317
+ const response = await fetch(`${this.baseUrl}/api/search`, {
318
+ method: 'POST',
319
+ headers: { 'Content-Type': 'application/json' },
320
+ body: JSON.stringify({ query, limit })
321
+ });
322
+ if (!response.ok) throw new Error(`HTTP ${response.status}`);
323
+ return await response.json();
324
+ }
325
+ }
326
+
327
+ // Initialize database
328
+ const db = new MockPluresDBLocalFirst();
329
+ window.db = db; // Make available to inline event handlers
330
+
331
+ // Update mode badge
332
+ const modeBadge = document.getElementById('mode-badge');
333
+ modeBadge.textContent = `${db.getMode().toUpperCase()} (Fallback)`;
334
+ modeBadge.className = 'status-badge fallback';
335
+
336
+ // Helper functions
337
+ function output(data) {
338
+ const outputEl = document.getElementById('output');
339
+ if (typeof data === 'object') {
340
+ outputEl.textContent = JSON.stringify(data, null, 2);
341
+ } else {
342
+ outputEl.textContent = data;
343
+ }
344
+ }
345
+
346
+ function updateNodeCount(count) {
347
+ document.getElementById('node-count').textContent = count;
348
+ }
349
+
350
+ // Action: Add User
351
+ window.addUser = async function() {
352
+ try {
353
+ const id = document.getElementById('userId').value;
354
+ const name = document.getElementById('userName').value;
355
+ const email = document.getElementById('userEmail').value;
356
+
357
+ if (!id || !name || !email) {
358
+ output('❌ Please fill in all fields');
359
+ return;
360
+ }
361
+
362
+ await db.put(id, {
363
+ type: 'User',
364
+ name,
365
+ email,
366
+ createdAt: new Date().toISOString()
367
+ });
368
+
369
+ output(`✅ User added successfully!\n\nID: ${id}\nName: ${name}\nEmail: ${email}`);
370
+
371
+ // Clear form
372
+ document.getElementById('userId').value = `user:${Date.now()}`;
373
+ document.getElementById('userName').value = '';
374
+ document.getElementById('userEmail').value = '';
375
+
376
+ // Refresh user list if visible
377
+ const usersSection = document.getElementById('usersSection');
378
+ if (usersSection.style.display !== 'none') {
379
+ await listUsers();
380
+ }
381
+ } catch (error) {
382
+ output(`❌ Error: ${error.message}`);
383
+ }
384
+ };
385
+
386
+ // Action: List Users
387
+ window.listUsers = async function() {
388
+ try {
389
+ const nodes = await db.list();
390
+ const users = nodes.filter(node => node.data?.type === 'User');
391
+
392
+ updateNodeCount(nodes.length);
393
+
394
+ if (users.length === 0) {
395
+ output('📭 No users found. Add some users first!');
396
+ document.getElementById('usersSection').style.display = 'none';
397
+ return;
398
+ }
399
+
400
+ // Show users in the dedicated section
401
+ const usersList = document.getElementById('usersList');
402
+ usersList.innerHTML = '';
403
+
404
+ users.forEach(user => {
405
+ const li = document.createElement('li');
406
+ li.className = 'user-item';
407
+ li.innerHTML = `
408
+ <div class="user-info">
409
+ <div class="user-name">${user.data.name}</div>
410
+ <div class="user-email">${user.data.email}</div>
411
+ </div>
412
+ <button class="delete-btn" onclick="deleteUser('${user.id}')">Delete</button>
413
+ `;
414
+ usersList.appendChild(li);
415
+ });
416
+
417
+ document.getElementById('usersSection').style.display = 'block';
418
+ output(`✅ Found ${users.length} user(s)\n${nodes.length} total nodes in database`);
419
+ } catch (error) {
420
+ output(`❌ Error: ${error.message}`);
421
+ }
422
+ };
423
+
424
+ // Action: Delete User
425
+ window.deleteUser = async function(id) {
426
+ try {
427
+ await db.delete(id);
428
+ output(`✅ User ${id} deleted successfully`);
429
+ await listUsers();
430
+ } catch (error) {
431
+ output(`❌ Error: ${error.message}`);
432
+ }
433
+ };
434
+
435
+ // Action: Vector Search Demo
436
+ window.searchUsers = async function() {
437
+ try {
438
+ const results = await db.vectorSearch('Find users', 10);
439
+ output({
440
+ query: 'Find users',
441
+ results: results.length,
442
+ data: results
443
+ });
444
+ } catch (error) {
445
+ output(`❌ Error: ${error.message}\n\nNote: Vector search requires the server to be running.`);
446
+ }
447
+ };
448
+
449
+ // Action: Clear Output
450
+ window.clearOutput = function() {
451
+ output('Output cleared.');
452
+ };
453
+
454
+ // Initialize - try to get node count
455
+ (async function() {
456
+ try {
457
+ const nodes = await db.list();
458
+ updateNodeCount(nodes.length);
459
+ output(`✅ Connected to PluresDB server\n${nodes.length} nodes in database\n\nMode: ${db.getMode()} (fallback)`);
460
+ } catch (error) {
461
+ output(`⚠️ PluresDB server not running\n\nPlease start it with: npm start\n\nOnce started, refresh this page.`);
462
+ }
463
+ })();
464
+ </script>
465
+ </body>
466
+ </html>