holosphere 2.0.0-alpha21 → 2.0.0-alpha22

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 (69) hide show
  1. package/README.md +1 -2
  2. package/dist/cjs/holosphere.cjs +1 -1
  3. package/dist/esm/holosphere.js +43 -41
  4. package/dist/{index-COpLk9gL.cjs → index-B4xe-N5-.cjs} +2 -2
  5. package/dist/{index-COpLk9gL.cjs.map → index-B4xe-N5-.cjs.map} +1 -1
  6. package/dist/{index-D2WstuZJ.js → index-Bug_CGNq.js} +2 -2
  7. package/dist/{index-D2WstuZJ.js.map → index-Bug_CGNq.js.map} +1 -1
  8. package/dist/index-CaCPzdlv.cjs +29 -0
  9. package/dist/index-CaCPzdlv.cjs.map +1 -0
  10. package/dist/{index-B6-8KAQm.js → index-D76zMgwU.js} +2 -2
  11. package/dist/{index-B6-8KAQm.js.map → index-D76zMgwU.js.map} +1 -1
  12. package/dist/{index--QsHG_gD.cjs → index-DuOuk96g.cjs} +2 -2
  13. package/dist/{index--QsHG_gD.cjs.map → index-DuOuk96g.cjs.map} +1 -1
  14. package/dist/{index-BHptWysv.js → index-bYHRpACA.js} +2951 -7736
  15. package/dist/index-bYHRpACA.js.map +1 -0
  16. package/dist/{indexeddb-storage-kQ53UHEE.js → indexeddb-storage-BrIwr42m.js} +2 -2
  17. package/dist/{indexeddb-storage-kQ53UHEE.js.map → indexeddb-storage-BrIwr42m.js.map} +1 -1
  18. package/dist/{indexeddb-storage-wKG4mICM.cjs → indexeddb-storage-CFWfkdX9.cjs} +2 -2
  19. package/dist/{indexeddb-storage-wKG4mICM.cjs.map → indexeddb-storage-CFWfkdX9.cjs.map} +1 -1
  20. package/dist/{memory-storage-DnXCSbBl.js → memory-storage-BDQRj-2j.js} +2 -2
  21. package/dist/{memory-storage-DnXCSbBl.js.map → memory-storage-BDQRj-2j.js.map} +1 -1
  22. package/dist/{memory-storage-CGC8xM2G.cjs → memory-storage-bkatDnuR.cjs} +2 -2
  23. package/dist/{memory-storage-CGC8xM2G.cjs.map → memory-storage-bkatDnuR.cjs.map} +1 -1
  24. package/examples/demo.html +2 -29
  25. package/package.json +3 -8
  26. package/src/content/social-protocols.js +3 -59
  27. package/src/core/holosphere.js +16 -554
  28. package/src/crypto/nostr-utils.js +98 -1
  29. package/src/crypto/secp256k1.js +4 -393
  30. package/src/federation/discovery.js +7 -75
  31. package/src/federation/handshake.js +69 -202
  32. package/src/federation/hologram.js +222 -298
  33. package/src/federation/index.js +2 -9
  34. package/src/federation/registry.js +67 -1257
  35. package/src/federation/request-card.js +21 -35
  36. package/src/hierarchical/upcast.js +4 -9
  37. package/src/index.js +142 -296
  38. package/src/lib/federation-methods.js +370 -909
  39. package/src/storage/global-tables.js +1 -1
  40. package/src/storage/nostr-wrapper.js +9 -5
  41. package/src/subscriptions/manager.js +1 -1
  42. package/types/index.d.ts +145 -37
  43. package/bin/holosphere-activitypub.js +0 -158
  44. package/dist/2019-BzVkRcax.js +0 -6680
  45. package/dist/2019-BzVkRcax.js.map +0 -1
  46. package/dist/2019-C1hPR_Os.cjs +0 -8
  47. package/dist/2019-C1hPR_Os.cjs.map +0 -1
  48. package/dist/browser-BcmACE3G.js +0 -3058
  49. package/dist/browser-BcmACE3G.js.map +0 -1
  50. package/dist/browser-DaqYUTcG.cjs +0 -2
  51. package/dist/browser-DaqYUTcG.cjs.map +0 -1
  52. package/dist/index-BHptWysv.js.map +0 -1
  53. package/dist/index-CDlhzxT2.cjs +0 -29
  54. package/dist/index-CDlhzxT2.cjs.map +0 -1
  55. package/src/federation/capabilities.js +0 -46
  56. package/src/storage/backend-factory.js +0 -130
  57. package/src/storage/backend-interface.js +0 -161
  58. package/src/storage/backends/activitypub/server.js +0 -675
  59. package/src/storage/backends/activitypub-backend.js +0 -295
  60. package/src/storage/backends/gundb-backend.js +0 -875
  61. package/src/storage/backends/nostr-backend.js +0 -251
  62. package/src/storage/gun-async.js +0 -341
  63. package/src/storage/gun-auth.js +0 -373
  64. package/src/storage/gun-federation.js +0 -785
  65. package/src/storage/gun-references.js +0 -209
  66. package/src/storage/gun-schema.js +0 -306
  67. package/src/storage/gun-wrapper.js +0 -642
  68. package/src/storage/migration.js +0 -351
  69. package/src/storage/unified-storage.js +0 -161
@@ -1,642 +0,0 @@
1
- /**
2
- * @fileoverview GunDB Storage Wrapper with radisk persistence.
3
- *
4
- * Handles path construction and CRUD operations for GunDB storage backend.
5
- * Note: GunDB doesn't handle nested objects well, so we store data as JSON strings.
6
- * Provides both holon-specific paths and global table operations.
7
- *
8
- * @module storage/gun-wrapper
9
- */
10
-
11
- import { gunPromise, gunPut, gunCollect } from './gun-async.js';
12
-
13
- // ============================================================================
14
- // PATH BUILDERS
15
- // ============================================================================
16
-
17
- /**
18
- * Build Gun path from components.
19
- *
20
- * @param {string} appname - Application namespace
21
- * @param {string} holon - Holon ID (H3 or URI)
22
- * @param {string} lens - Lens name
23
- * @param {string} [key=null] - Data key (optional)
24
- * @returns {string} Gun path
25
- * @example
26
- * const path = buildPath('myapp', 'holon123', 'items', 'item1');
27
- * // Returns: 'myapp/holon123/items/item1'
28
- */
29
- export function buildPath(appname, holon, lens, key = null) {
30
- // Encode components to handle special characters
31
- const encodedHolon = encodePathComponent(holon);
32
- const encodedLens = encodePathComponent(lens);
33
-
34
- if (key) {
35
- const encodedKey = encodePathComponent(key);
36
- return `${appname}/${encodedHolon}/${encodedLens}/${encodedKey}`;
37
- }
38
- return `${appname}/${encodedHolon}/${encodedLens}`;
39
- }
40
-
41
- /**
42
- * Build Gun path for global tables (app-wide data not tied to holons).
43
- *
44
- * @param {string} appname - Application namespace
45
- * @param {string} tableName - Global table name (e.g., 'schemas', 'federation')
46
- * @param {string} [key=null] - Data key (optional)
47
- * @returns {string} Gun path
48
- * @example
49
- * const path = buildGlobalPath('myapp', 'schemas', 'user');
50
- * // Returns: 'myapp/schemas/user'
51
- */
52
- export function buildGlobalPath(appname, tableName, key = null) {
53
- const encodedTable = encodePathComponent(tableName);
54
- if (key) {
55
- const encodedKey = encodePathComponent(key);
56
- return `${appname}/${encodedTable}/${encodedKey}`;
57
- }
58
- return `${appname}/${encodedTable}`;
59
- }
60
-
61
- /**
62
- * Encode path component to handle special characters.
63
- *
64
- * @private
65
- * @param {string} component - Component to encode
66
- * @returns {string} Encoded component
67
- */
68
- function encodePathComponent(component) {
69
- return encodeURIComponent(component).replace(/%2F/g, '/');
70
- }
71
-
72
- /**
73
- * Navigate to a Gun path using chained .get() calls.
74
- *
75
- * Gun treats 'a/b/c' as a literal key, not a path.
76
- * This function splits the path and chains .get() calls properly.
77
- *
78
- * @private
79
- * @param {Object} gun - Gun instance
80
- * @param {string} path - Path string like "appname/holon/lens/key"
81
- * @returns {Object} Gun chain reference at the path
82
- */
83
- function getGunPath(gun, path) {
84
- const parts = path.split('/').filter(p => p.length > 0);
85
- let ref = gun;
86
- for (const part of parts) {
87
- ref = ref.get(part);
88
- }
89
- return ref;
90
- }
91
-
92
- /**
93
- * Serialize data for GunDB storage.
94
- *
95
- * Stores data as raw JSON string for compatibility with holosphere original.
96
- * This matches the format used in holosphere v1 for better interoperability.
97
- *
98
- * @private
99
- * @param {Object} data - Data to serialize
100
- * @returns {string} JSON string
101
- */
102
- function serializeForGun(data) {
103
- return JSON.stringify(data);
104
- }
105
-
106
- /**
107
- * Deserialize data from GunDB storage.
108
- *
109
- * Handles multiple formats:
110
- * - Direct JSON string (holosphere original - now default)
111
- * - _json wrapped format (holosphere2 legacy)
112
- * - Gun internal references (_["#"])
113
- * - Gun node data (_[">"])
114
- * - Plain objects
115
- *
116
- * @private
117
- * @param {*} data - Raw data from Gun
118
- * @returns {Object|null} Parsed data or null
119
- */
120
- function deserializeFromGun(data) {
121
- if (!data) {
122
- return null;
123
- }
124
-
125
- try {
126
- // Format 1: String data (holosphere original stores JSON as string directly)
127
- if (typeof data === 'string') {
128
- try {
129
- return JSON.parse(data);
130
- } catch (e) {
131
- // Not JSON, return as-is
132
- return data;
133
- }
134
- }
135
-
136
- // Format 2: _json wrapped format (holosphere2 default)
137
- if (data._json && typeof data._json === 'string') {
138
- try {
139
- return JSON.parse(data._json);
140
- } catch (e) {
141
- console.warn('Failed to parse _json field:', e);
142
- return null;
143
- }
144
- }
145
-
146
- // Format 3: Gun internal reference (_["#"]) - this indicates a Gun reference
147
- // The actual data retrieval should happen via resolveReference, we just identify it here
148
- if (data._ && data._['#']) {
149
- // This is a Gun reference - return as-is for reference resolution
150
- // The caller should check isReference() and handle appropriately
151
- return data;
152
- }
153
-
154
- // Format 4: Gun node data with timestamps (_[">"])
155
- // Gun stores metadata in _.> - find the actual value
156
- if (data._ && data._['>']) {
157
- const nodeValue = Object.entries(data).find(([k, v]) => k !== '_' && typeof v === 'string');
158
- if (nodeValue) {
159
- try {
160
- return JSON.parse(nodeValue[1]);
161
- } catch (e) {
162
- return nodeValue[1];
163
- }
164
- }
165
- }
166
-
167
- // Format 5: Plain object - clean Gun metadata and return
168
- if (typeof data === 'object' && data !== null) {
169
- const cleaned = { ...data };
170
- delete cleaned['_'];
171
-
172
- // Check if any remaining keys - if empty after removing _, return null
173
- if (Object.keys(cleaned).length === 0) {
174
- return null;
175
- }
176
-
177
- return cleaned;
178
- }
179
-
180
- return data;
181
- } catch (error) {
182
- console.warn('Error deserializing Gun data:', error);
183
- return data; // Return raw data as fallback
184
- }
185
- }
186
-
187
- /**
188
- * Write data to Gun with radisk persistence.
189
- *
190
- * @param {Object} gun - Gun instance
191
- * @param {string} path - Gun path
192
- * @param {Object} data - Data to write
193
- * @returns {Promise<Object>} Success object with ok and timeout flags
194
- * @example
195
- * const result = await write(gun, 'myapp/holon1/items/item1', { name: 'Test' });
196
- * if (result.timeout) console.warn('Write may not be persisted');
197
- */
198
- export async function write(gun, path, data) {
199
- try {
200
- const serialized = serializeForGun(data);
201
- const parts = path.split('/').filter(p => p.length > 0);
202
- console.log('[gun-wrapper] write:', { path, parts, dataId: data?.id });
203
- const ref = getGunPath(gun, path);
204
- console.log('[gun-wrapper] write ref soul:', ref?._.get);
205
- const ack = await gunPut(ref, serialized, 5000); // Increased timeout from 2s to 5s
206
- console.log('[gun-wrapper] write ack:', { ok: ack.ok, timeout: ack.timeout });
207
- if (ack.timeout) {
208
- console.warn('[gun-wrapper] write timed out (data may not be persisted):', path);
209
- }
210
- console.log('[gun-wrapper] write complete:', path);
211
-
212
- // Return ack info so caller can handle timeouts
213
- return { ok: true, timeout: ack.timeout || false };
214
- } catch (error) {
215
- console.error('[gun-wrapper] write error:', error);
216
- throw error;
217
- }
218
- }
219
-
220
- /**
221
- * Read data from Gun.
222
- *
223
- * @param {Object} gun - Gun instance
224
- * @param {string} path - Gun path
225
- * @returns {Promise<Object|null>} Data or null if not found or deleted
226
- * @example
227
- * const data = await read(gun, 'myapp/holon1/items/item1');
228
- * if (data) console.log(data.name);
229
- */
230
- export async function read(gun, path) {
231
- const parts = path.split('/').filter(p => p.length > 0);
232
- console.log('[gun-wrapper] read:', { path, parts });
233
- const ref = getGunPath(gun, path);
234
- console.log('[gun-wrapper] read ref soul:', ref?._.get);
235
- const rawData = await gunPromise(ref, 2000);
236
- console.log('[gun-wrapper] read rawData:', rawData ? (typeof rawData === 'string' ? rawData.substring(0, 100) : 'object') : 'null');
237
-
238
- if (!rawData) {
239
- return null;
240
- }
241
-
242
- const data = deserializeFromGun(rawData);
243
-
244
- // Return null if deleted or not found
245
- if (!data || data._deleted) {
246
- return null;
247
- }
248
-
249
- return data;
250
- }
251
-
252
- /**
253
- * Read all data under a path (lens query).
254
- *
255
- * First gets the count of expected items, then collects until count is reached.
256
- *
257
- * @param {Object} gun - Gun instance
258
- * @param {string} path - Gun path
259
- * @param {number} [timeout=5000] - Maximum timeout in ms
260
- * @returns {Promise<Object[]>} Array of data objects
261
- * @example
262
- * const items = await readAll(gun, 'myapp/holon1/items');
263
- * console.log(`Found ${items.length} items`);
264
- */
265
- export async function readAll(gun, path, timeout = 5000) {
266
- const parts = path.split('/').filter(p => p.length > 0);
267
- console.log('[gun-wrapper] readAll:', { path, parts });
268
-
269
- return new Promise((resolve) => {
270
- const output = new Map();
271
- let settled = false;
272
- let expectedCount = 0;
273
- let receivedCount = 0;
274
-
275
- const ref = getGunPath(gun, path);
276
- console.log('[gun-wrapper] readAll ref soul:', ref?._.get);
277
-
278
- const tryResolve = () => {
279
- if (settled) return;
280
- if (expectedCount > 0 && receivedCount >= expectedCount) {
281
- settled = true;
282
- console.log('[gun-wrapper] readAll resolved with', output.size, 'items');
283
- resolve(Array.from(output.values()));
284
- }
285
- };
286
-
287
- const parseItem = (data) => {
288
- return deserializeFromGun(data);
289
- };
290
-
291
- // Step 1: Get the parent data to count expected items
292
- ref.once((parentData) => {
293
- if (settled) return;
294
- console.log('[gun-wrapper] readAll parentData:', parentData);
295
- console.log('[gun-wrapper] readAll parentData keys:', parentData ? Object.keys(parentData).filter(k => k !== '_') : 'null');
296
- console.log('[gun-wrapper] readAll parentData type:', typeof parentData);
297
-
298
- if (!parentData) {
299
- settled = true;
300
- console.log('[gun-wrapper] readAll: no parent data, returning empty');
301
- resolve([]);
302
- return;
303
- }
304
-
305
- // Get all keys except Gun metadata
306
- const keys = Object.keys(parentData).filter(k => k !== '_');
307
- console.log('[gun-wrapper] readAll keys:', keys);
308
-
309
- if (keys.length === 0) {
310
- settled = true;
311
- console.log('[gun-wrapper] readAll: no keys, returning empty');
312
- resolve([]);
313
- return;
314
- }
315
-
316
- // Count expected items and check for inline data
317
- const referenceKeys = [];
318
-
319
- for (const key of keys) {
320
- const rawItem = parentData[key];
321
- if (!rawItem) continue;
322
-
323
- // Check if it's a Gun reference (soul) - need to fetch separately
324
- if (typeof rawItem === 'object' && rawItem['#']) {
325
- referenceKeys.push(key);
326
- continue;
327
- }
328
-
329
- // Try to parse inline data (don't count yet - will count in map().once() phase)
330
- const item = parseItem(rawItem);
331
- if (item && item.id && !item._deleted) {
332
- output.set(item.id, item);
333
- }
334
- }
335
-
336
- // Set expected count: ALL keys that could have data (references + inline)
337
- // We use total keys because map().once() will fire for all of them
338
- expectedCount = keys.length;
339
-
340
- // If no keys, we're done (shouldn't happen but be safe)
341
- if (expectedCount === 0) {
342
- settled = true;
343
- resolve(Array.from(output.values()));
344
- return;
345
- }
346
-
347
- // Step 2: Use map().once() to resolve all items, counting as we go
348
- ref.map().once((data, key) => {
349
- if (settled || !data || key === '_') return;
350
-
351
- const item = parseItem(data);
352
- if (item && item.id && !item._deleted) {
353
- // Add to output if not already there (inline items already added)
354
- if (!output.has(item.id)) {
355
- output.set(item.id, item);
356
- }
357
- }
358
- // Count every item received (inline or reference)
359
- receivedCount++;
360
- tryResolve();
361
- });
362
- });
363
-
364
- // Fallback timeout in case count-based resolution fails
365
- setTimeout(() => {
366
- if (!settled) {
367
- settled = true;
368
- resolve(Array.from(output.values()));
369
- }
370
- }, timeout);
371
- });
372
- }
373
-
374
- /**
375
- * Update data (merge fields).
376
- *
377
- * @param {Object} gun - Gun instance
378
- * @param {string} path - Gun path
379
- * @param {Object} updates - Fields to update
380
- * @returns {Promise<boolean>} Success indicator
381
- * @example
382
- * const success = await update(gun, 'myapp/holon1/items/item1', { status: 'active' });
383
- */
384
- export async function update(gun, path, updates) {
385
- const rawData = await gunPromise(getGunPath(gun, path));
386
-
387
- if (!rawData) {
388
- return false; // Not found
389
- }
390
-
391
- // Deserialize existing data
392
- const existing = deserializeFromGun(rawData);
393
- if (!existing || !existing.id || existing._deleted) {
394
- return false;
395
- }
396
-
397
- // Merge updates
398
- const merged = { ...existing, ...updates };
399
-
400
- try {
401
- const serialized = serializeForGun(merged);
402
- await gunPut(getGunPath(gun, path), serialized, 2000);
403
- // Add delay for propagation
404
- await new Promise(resolve => setTimeout(resolve, 200));
405
- return true;
406
- } catch (error) {
407
- throw error;
408
- }
409
- }
410
-
411
- /**
412
- * Delete data (tombstone).
413
- *
414
- * @param {Object} gun - Gun instance
415
- * @param {string} path - Gun path
416
- * @returns {Promise<boolean>} Success indicator
417
- * @example
418
- * const deleted = await deleteData(gun, 'myapp/holon1/items/item1');
419
- */
420
- export async function deleteData(gun, path) {
421
- try {
422
- // First read existing data to get the id
423
- const rawData = await gunPromise(getGunPath(gun, path));
424
- if (!rawData) {
425
- return true; // Already deleted/doesn't exist
426
- }
427
-
428
- const existing = deserializeFromGun(rawData);
429
-
430
- // Create tombstone object and serialize it
431
- const tombstone = {
432
- id: existing?.id,
433
- _deleted: true,
434
- _deletedAt: Date.now()
435
- };
436
-
437
- await gunPut(getGunPath(gun, path), serializeForGun(tombstone), 2000);
438
- // Add delay for propagation
439
- await new Promise(resolve => setTimeout(resolve, 200));
440
- return true;
441
- } catch (error) {
442
- throw error;
443
- }
444
- }
445
-
446
- /**
447
- * Delete all data under path prefix (tombstone).
448
- *
449
- * @param {Object} gun - Gun instance
450
- * @param {string} path - Gun path prefix
451
- * @returns {Promise<Object>} Deletion results { success: boolean, count: number }
452
- */
453
- export async function deleteAll(gun, path) {
454
- const items = await readAll(gun, path);
455
- let count = 0;
456
-
457
- for (const item of items) {
458
- if (item && item.id) {
459
- const itemPath = `${path}/${item.id}`;
460
- await deleteData(gun, itemPath);
461
- count++;
462
- }
463
- }
464
-
465
- return { success: true, count };
466
- }
467
-
468
- /**
469
- * Subscribe to data changes.
470
- *
471
- * @param {Object} gun - Gun instance
472
- * @param {string} path - Gun path
473
- * @param {Function} callback - Called on data changes (data, key) => void
474
- * @param {Object} [options={}] - Subscription options
475
- * @param {boolean} [options.prefix] - Subscribe to all items under path (default: auto-detect)
476
- * @returns {Object} Subscription object with unsubscribe method
477
- * @example
478
- * const sub = subscribe(gun, 'myapp/holon1/items', (data, key) => {
479
- * console.log('Item changed:', key, data);
480
- * });
481
- * // Later: sub.unsubscribe();
482
- */
483
- export function subscribe(gun, path, callback, options = {}) {
484
- // Detect if this is a prefix subscription
485
- const pathParts = path.split('/');
486
- const isPrefix = options.prefix !== undefined ? options.prefix : pathParts.length <= 3;
487
-
488
- if (isPrefix) {
489
- // Subscribe to all items under this prefix
490
- const ref = getGunPath(gun, path);
491
-
492
- ref.map().on((data, key) => {
493
- if (data && !key.startsWith('_')) {
494
- const deserialized = deserializeFromGun(data);
495
- if (deserialized && !deserialized._deleted) {
496
- callback(deserialized, key);
497
- }
498
- }
499
- });
500
-
501
- return {
502
- unsubscribe: () => {
503
- try {
504
- ref.off();
505
- } catch (e) {
506
- // Ignore cleanup errors
507
- }
508
- },
509
- };
510
- } else {
511
- // Subscribe to single item
512
- const listener = getGunPath(gun, path).on((data, key) => {
513
- if (data) {
514
- const deserialized = deserializeFromGun(data);
515
- if (deserialized && !deserialized._deleted) {
516
- callback(deserialized, key);
517
- }
518
- }
519
- });
520
-
521
- return {
522
- unsubscribe: () => {
523
- try {
524
- listener.off();
525
- } catch (e) {
526
- // Ignore cleanup errors
527
- }
528
- },
529
- };
530
- }
531
- }
532
-
533
- // ============================================================================
534
- // GLOBAL TABLE OPERATIONS
535
- // ============================================================================
536
-
537
- /**
538
- * Write data to a global table.
539
- *
540
- * Global tables are app-wide data not tied to specific holons (e.g., schemas, federation).
541
- *
542
- * @param {Object} gun - Gun instance
543
- * @param {string} appname - Application namespace
544
- * @param {string} tableName - Global table name
545
- * @param {Object} data - Data to write (must have 'id' field)
546
- * @returns {Promise<Object>} Success object with ok and timeout flags
547
- * @throws {Error} If data doesn't have an id field
548
- * @example
549
- * await writeGlobal(gun, 'myapp', 'schemas', { id: 'user', type: 'object' });
550
- */
551
- export async function writeGlobal(gun, appname, tableName, data) {
552
- if (!data || !data.id) {
553
- throw new Error('writeGlobal: data must have an id field');
554
- }
555
- const path = buildGlobalPath(appname, tableName, data.id);
556
- // Use write function which includes the propagation delay
557
- return write(gun, path, data);
558
- }
559
-
560
- /**
561
- * Read data from a global table.
562
- *
563
- * @param {Object} gun - Gun instance
564
- * @param {string} appname - Application namespace
565
- * @param {string} tableName - Global table name
566
- * @param {string} key - Data key
567
- * @returns {Promise<Object|null>} Data or null if not found
568
- */
569
- export async function readGlobal(gun, appname, tableName, key) {
570
- const path = buildGlobalPath(appname, tableName, key);
571
- return read(gun, path);
572
- }
573
-
574
- /**
575
- * Read all data from a global table.
576
- *
577
- * Uses same approach as readAll.
578
- *
579
- * @param {Object} gun - Gun instance
580
- * @param {string} appname - Application namespace
581
- * @param {string} tableName - Global table name
582
- * @param {number} [timeout=2000] - Timeout in ms
583
- * @returns {Promise<Object[]>} Array of data objects
584
- */
585
- export async function readAllGlobal(gun, appname, tableName, timeout = 2000) {
586
- const path = buildGlobalPath(appname, tableName);
587
- return readAll(gun, path);
588
- }
589
-
590
- /**
591
- * Delete data from a global table
592
- * @param {Object} gun - Gun instance
593
- * @param {string} appname - Application namespace
594
- * @param {string} tableName - Global table name
595
- * @param {string} key - Data key
596
- * @returns {Promise<boolean>} Success indicator
597
- */
598
- export async function deleteGlobal(gun, appname, tableName, key) {
599
- const path = buildGlobalPath(appname, tableName, key);
600
- return deleteData(gun, path);
601
- }
602
-
603
- /**
604
- * Delete all data from a global table
605
- * @param {Object} gun - Gun instance
606
- * @param {string} appname - Application namespace
607
- * @param {string} tableName - Global table name
608
- * @returns {Promise<Object>} Deletion results { success: boolean, count: number }
609
- */
610
- export async function deleteAllGlobal(gun, appname, tableName) {
611
- const path = buildGlobalPath(appname, tableName);
612
- return deleteAll(gun, path);
613
- }
614
-
615
- // ============================================================================
616
- // DATA PARSING UTILITIES
617
- // ============================================================================
618
-
619
- /**
620
- * Parse data from Gun storage, handling various formats.
621
- *
622
- * Handles:
623
- * - JSON string in _json field
624
- * - Legacy object format
625
- * - Gun references
626
- *
627
- * @param {*} rawData - Raw data from Gun
628
- * @returns {Object|null} Parsed data or null
629
- */
630
- export function parse(rawData) {
631
- return deserializeFromGun(rawData);
632
- }
633
-
634
- /**
635
- * Serialize data for Gun storage.
636
- *
637
- * @param {Object} data - Data to serialize
638
- * @returns {string} JSON string
639
- */
640
- export function serialize(data) {
641
- return serializeForGun(data);
642
- }