holosphere 2.0.0-alpha2 → 2.0.0-alpha4

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 (98) hide show
  1. package/dist/2019-D2OG2idw.js +6680 -0
  2. package/dist/2019-D2OG2idw.js.map +1 -0
  3. package/dist/2019-EION3wKo.cjs +8 -0
  4. package/dist/2019-EION3wKo.cjs.map +1 -0
  5. package/dist/_commonjsHelpers-C37NGDzP.cjs +2 -0
  6. package/dist/_commonjsHelpers-C37NGDzP.cjs.map +1 -0
  7. package/dist/_commonjsHelpers-CUmg6egw.js +7 -0
  8. package/dist/_commonjsHelpers-CUmg6egw.js.map +1 -0
  9. package/dist/browser-BSniCNqO.js +3058 -0
  10. package/dist/browser-BSniCNqO.js.map +1 -0
  11. package/dist/browser-Cq59Ij19.cjs +2 -0
  12. package/dist/browser-Cq59Ij19.cjs.map +1 -0
  13. package/dist/cjs/holosphere.cjs +1 -1
  14. package/dist/esm/holosphere.js +50 -53
  15. package/dist/index-BB_vVJgv.cjs +5 -0
  16. package/dist/index-BB_vVJgv.cjs.map +1 -0
  17. package/dist/index-CBitK71M.cjs +12 -0
  18. package/dist/index-CBitK71M.cjs.map +1 -0
  19. package/dist/index-CV0eOogK.js +37423 -0
  20. package/dist/index-CV0eOogK.js.map +1 -0
  21. package/dist/index-Cz-PLCUR.js +15104 -0
  22. package/dist/index-Cz-PLCUR.js.map +1 -0
  23. package/dist/indexeddb-storage-CRsZyB2f.cjs +2 -0
  24. package/dist/indexeddb-storage-CRsZyB2f.cjs.map +1 -0
  25. package/dist/{indexeddb-storage-CMW4qRQS.js → indexeddb-storage-DZaGlY_a.js} +49 -13
  26. package/dist/indexeddb-storage-DZaGlY_a.js.map +1 -0
  27. package/dist/{memory-storage-DQzcAZlf.js → memory-storage-BkUi6sZG.js} +6 -2
  28. package/dist/memory-storage-BkUi6sZG.js.map +1 -0
  29. package/dist/{memory-storage-DmePEP2q.cjs → memory-storage-C0DuUsdY.cjs} +2 -2
  30. package/dist/memory-storage-C0DuUsdY.cjs.map +1 -0
  31. package/dist/secp256k1-0kPdAVkK.cjs +12 -0
  32. package/dist/secp256k1-0kPdAVkK.cjs.map +1 -0
  33. package/dist/{secp256k1-vOXp40Fx.js → secp256k1-DN4FVXcv.js} +2 -393
  34. package/dist/secp256k1-DN4FVXcv.js.map +1 -0
  35. package/docs/CONTRACTS.md +797 -0
  36. package/examples/demo.html +47 -0
  37. package/package.json +10 -5
  38. package/src/contracts/abis/Appreciative.json +1280 -0
  39. package/src/contracts/abis/AppreciativeFactory.json +101 -0
  40. package/src/contracts/abis/Bundle.json +1435 -0
  41. package/src/contracts/abis/BundleFactory.json +106 -0
  42. package/src/contracts/abis/Holon.json +881 -0
  43. package/src/contracts/abis/Holons.json +330 -0
  44. package/src/contracts/abis/Managed.json +1262 -0
  45. package/src/contracts/abis/ManagedFactory.json +149 -0
  46. package/src/contracts/abis/Membrane.json +261 -0
  47. package/src/contracts/abis/Splitter.json +1624 -0
  48. package/src/contracts/abis/SplitterFactory.json +220 -0
  49. package/src/contracts/abis/TestToken.json +321 -0
  50. package/src/contracts/abis/Zoned.json +1461 -0
  51. package/src/contracts/abis/ZonedFactory.json +154 -0
  52. package/src/contracts/chain-manager.js +375 -0
  53. package/src/contracts/deployer.js +443 -0
  54. package/src/contracts/event-listener.js +507 -0
  55. package/src/contracts/holon-contracts.js +344 -0
  56. package/src/contracts/index.js +83 -0
  57. package/src/contracts/networks.js +224 -0
  58. package/src/contracts/operations.js +670 -0
  59. package/src/contracts/queries.js +589 -0
  60. package/src/core/holosphere.js +453 -1
  61. package/src/crypto/nostr-utils.js +263 -0
  62. package/src/federation/handshake.js +455 -0
  63. package/src/federation/hologram.js +1 -1
  64. package/src/hierarchical/upcast.js +6 -5
  65. package/src/index.js +463 -1939
  66. package/src/lib/ai-methods.js +308 -0
  67. package/src/lib/contract-methods.js +293 -0
  68. package/src/lib/errors.js +23 -0
  69. package/src/lib/federation-methods.js +238 -0
  70. package/src/lib/index.js +26 -0
  71. package/src/spatial/h3-operations.js +2 -2
  72. package/src/storage/backends/gundb-backend.js +377 -46
  73. package/src/storage/global-tables.js +28 -1
  74. package/src/storage/gun-auth.js +303 -0
  75. package/src/storage/gun-federation.js +776 -0
  76. package/src/storage/gun-references.js +198 -0
  77. package/src/storage/gun-schema.js +291 -0
  78. package/src/storage/gun-wrapper.js +347 -31
  79. package/src/storage/indexeddb-storage.js +49 -11
  80. package/src/storage/memory-storage.js +5 -0
  81. package/src/storage/nostr-async.js +45 -23
  82. package/src/storage/nostr-client.js +11 -5
  83. package/src/storage/persistent-storage.js +6 -1
  84. package/src/storage/unified-storage.js +119 -0
  85. package/src/subscriptions/manager.js +1 -1
  86. package/types/index.d.ts +133 -0
  87. package/dist/index-CDfIuXew.js +0 -15974
  88. package/dist/index-CDfIuXew.js.map +0 -1
  89. package/dist/index-ifOgtDvd.cjs +0 -3
  90. package/dist/index-ifOgtDvd.cjs.map +0 -1
  91. package/dist/indexeddb-storage-CMW4qRQS.js.map +0 -1
  92. package/dist/indexeddb-storage-DLZOgetM.cjs +0 -2
  93. package/dist/indexeddb-storage-DLZOgetM.cjs.map +0 -1
  94. package/dist/memory-storage-DQzcAZlf.js.map +0 -1
  95. package/dist/memory-storage-DmePEP2q.cjs.map +0 -1
  96. package/dist/secp256k1-CP0ZkpAx.cjs +0 -13
  97. package/dist/secp256k1-CP0ZkpAx.cjs.map +0 -1
  98. package/dist/secp256k1-vOXp40Fx.js.map +0 -1
@@ -1,17 +1,30 @@
1
1
  /**
2
2
  * GunDB Storage Backend
3
- * Wraps existing gun-wrapper.js as a StorageBackend
3
+ * Full-featured backend with authentication, schema validation, references, and global tables
4
4
  */
5
5
 
6
6
  import { StorageBackend } from '../backend-interface.js';
7
7
  import * as wrapper from '../gun-wrapper.js';
8
8
  import { gunPromise, gunPut, gunMap, gunCollect } from '../gun-async.js';
9
+ import { GunReferenceHandler } from '../gun-references.js';
10
+ import { GunAuth } from '../gun-auth.js';
11
+ import { GunSchemaValidator } from '../gun-schema.js';
9
12
 
10
13
  export class GunDBBackend extends StorageBackend {
11
14
  constructor(config) {
12
15
  super(config);
13
16
  this.gun = null;
14
17
  this.keyPair = null;
18
+ this.appName = config.appName || 'holosphere';
19
+
20
+ // New modules
21
+ this.references = null;
22
+ this.auth = null;
23
+ this.schemaValidator = null;
24
+
25
+ // Subscription tracking
26
+ this.subscriptions = new Map();
27
+ this.subscriptionCounter = 0;
15
28
  }
16
29
 
17
30
  async init() {
@@ -50,28 +63,93 @@ export class GunDBBackend extends StorageBackend {
50
63
  }
51
64
  } else {
52
65
  // SEA not available, use simple identifier
53
- this.publicKey = this.config.appName || 'gundb-user';
66
+ this.publicKey = this.appName;
54
67
  }
55
68
  } catch (error) {
56
69
  // SEA might not be loaded
57
- this.publicKey = this.config.appName || 'gundb-user';
70
+ this.publicKey = this.appName;
58
71
  }
72
+
73
+ // Initialize modules
74
+ this.references = new GunReferenceHandler(this.appName);
75
+ this.auth = new GunAuth(this.gun, this.appName);
76
+ this.schemaValidator = new GunSchemaValidator({
77
+ strict: this.config.strict || false,
78
+ cacheMaxAge: this.config.schemaCacheMaxAge || 3600000
79
+ });
80
+
81
+ // Try to initialize schema validator (may fail if AJV not installed)
82
+ await this.schemaValidator.init();
59
83
  }
60
84
 
85
+ // ============================================================================
86
+ // PATH BUILDING
87
+ // ============================================================================
88
+
61
89
  buildPath(appName, holonId, lensName, key = null) {
62
90
  return wrapper.buildPath(appName, holonId, lensName, key);
63
91
  }
64
92
 
93
+ buildGlobalPath(tableName, key = null) {
94
+ return wrapper.buildGlobalPath(this.appName, tableName, key);
95
+ }
96
+
97
+ // ============================================================================
98
+ // BASIC CRUD OPERATIONS
99
+ // ============================================================================
100
+
65
101
  async write(path, data, options = {}) {
102
+ // Validate against schema if strict mode
103
+ if (this.schemaValidator && this.schemaValidator.isStrict()) {
104
+ const pathParts = path.split('/');
105
+ if (pathParts.length >= 3) {
106
+ const lens = pathParts[2];
107
+ // Skip validation for references
108
+ if (!this.isReference(data)) {
109
+ const result = await this.schemaValidator.validateData(
110
+ this.gun, this.appName, lens, data
111
+ );
112
+ if (!result.valid) {
113
+ throw new Error(`Schema validation failed: ${JSON.stringify(result.errors)}`);
114
+ }
115
+ }
116
+ }
117
+ }
118
+
66
119
  return wrapper.write(this.gun, path, data);
67
120
  }
68
121
 
69
122
  async read(path, options = {}) {
70
- return wrapper.read(this.gun, path);
123
+ const data = await wrapper.read(this.gun, path);
124
+
125
+ // Resolve references if requested
126
+ if (data && options.resolveReferences && this.isReference(data)) {
127
+ return this.resolveReference(data);
128
+ }
129
+
130
+ return data;
71
131
  }
72
132
 
73
133
  async readAll(path, options = {}) {
74
- return wrapper.readAll(this.gun, path);
134
+ const items = await wrapper.readAll(this.gun, path);
135
+
136
+ // Resolve references if requested
137
+ if (options.resolveReferences) {
138
+ const resolved = [];
139
+ for (const item of items) {
140
+ if (this.isReference(item)) {
141
+ const resolvedItem = await this.resolveReference(item);
142
+ if (resolvedItem) {
143
+ resolved.push(resolvedItem);
144
+ }
145
+ } else {
146
+ resolved.push(item);
147
+ }
148
+ }
149
+ return resolved;
150
+ }
151
+
152
+ return items;
75
153
  }
76
154
 
77
155
  async update(path, updates) {
@@ -83,7 +161,6 @@ export class GunDBBackend extends StorageBackend {
83
161
  }
84
162
 
85
163
  async deleteAll(path) {
86
- // Gun doesn't have a native deleteAll, so we iterate and delete
87
164
  const items = await this.readAll(path);
88
165
  let count = 0;
89
166
 
@@ -98,58 +175,272 @@ export class GunDBBackend extends StorageBackend {
98
175
  return { success: true, count };
99
176
  }
100
177
 
178
+ // ============================================================================
179
+ // GLOBAL TABLE OPERATIONS
180
+ // ============================================================================
181
+
182
+ async writeGlobal(tableName, data) {
183
+ return wrapper.writeGlobal(this.gun, this.appName, tableName, data);
184
+ }
185
+
186
+ async readGlobal(tableName, key) {
187
+ return wrapper.readGlobal(this.gun, this.appName, tableName, key);
188
+ }
189
+
190
+ async readAllGlobal(tableName, timeout = 5000) {
191
+ return wrapper.readAllGlobal(this.gun, this.appName, tableName, timeout);
192
+ }
193
+
194
+ async deleteGlobal(tableName, key) {
195
+ return wrapper.deleteGlobal(this.gun, this.appName, tableName, key);
196
+ }
197
+
198
+ async deleteAllGlobal(tableName) {
199
+ return wrapper.deleteAllGlobal(this.gun, this.appName, tableName);
200
+ }
201
+
202
+ // ============================================================================
203
+ // REFERENCE OPERATIONS
204
+ // ============================================================================
205
+
206
+ /**
207
+ * Create a reference to data
208
+ */
209
+ createReference(holon, lens, data) {
210
+ return this.references.createReference(holon, lens, data);
211
+ }
212
+
213
+ /**
214
+ * Check if data is a reference
215
+ */
216
+ isReference(data) {
217
+ return this.references.isReference(data);
218
+ }
219
+
220
+ /**
221
+ * Resolve a reference to get actual data
222
+ */
223
+ async resolveReference(reference, options = {}) {
224
+ return this.references.resolveReference(this.gun, reference, options);
225
+ }
226
+
227
+ /**
228
+ * Parse a soul path
229
+ */
230
+ parseSoulPath(soul) {
231
+ return this.references.parseSoulPath(soul);
232
+ }
233
+
234
+ /**
235
+ * Get soul for data
236
+ */
237
+ getSoul(holon, lens, key) {
238
+ return this.references.getSoul(holon, lens, key);
239
+ }
240
+
241
+ // ============================================================================
242
+ // AUTHENTICATION OPERATIONS
243
+ // ============================================================================
244
+
245
+ /**
246
+ * Authenticate for a holon
247
+ */
248
+ async authenticate(holonId, password) {
249
+ return this.auth.authenticate(holonId, password);
250
+ }
251
+
252
+ /**
253
+ * Create a new user
254
+ */
255
+ async createUser(holonId, password) {
256
+ return this.auth.createUser(holonId, password);
257
+ }
258
+
259
+ /**
260
+ * Logout current user
261
+ */
262
+ logout() {
263
+ this.auth.logout();
264
+ }
265
+
266
+ /**
267
+ * Check if authenticated
268
+ */
269
+ isAuthenticated() {
270
+ return this.auth.isAuthenticated();
271
+ }
272
+
273
+ /**
274
+ * Write to private storage
275
+ */
276
+ async writePrivate(lens, key, data) {
277
+ return this.auth.writePrivate(lens, key, data);
278
+ }
279
+
280
+ /**
281
+ * Read from private storage
282
+ */
283
+ async readPrivate(lens, key) {
284
+ return this.auth.readPrivate(lens, key);
285
+ }
286
+
287
+ /**
288
+ * Read all from private lens
289
+ */
290
+ async readAllPrivate(lens, timeout = 2000) {
291
+ return this.auth.readAllPrivate(lens, timeout);
292
+ }
293
+
294
+ /**
295
+ * Delete from private storage
296
+ */
297
+ async deletePrivate(lens, key) {
298
+ return this.auth.deletePrivate(lens, key);
299
+ }
300
+
301
+ // ============================================================================
302
+ // SCHEMA OPERATIONS
303
+ // ============================================================================
304
+
305
+ /**
306
+ * Set a schema for a lens
307
+ */
308
+ async setSchema(lens, schema) {
309
+ return this.schemaValidator.setSchema(this.gun, this.appName, lens, schema);
310
+ }
311
+
312
+ /**
313
+ * Get a schema for a lens
314
+ */
315
+ async getSchema(lens, options = {}) {
316
+ return this.schemaValidator.getSchema(this.gun, this.appName, lens, options);
317
+ }
318
+
319
+ /**
320
+ * Get all schemas
321
+ */
322
+ async getAllSchemas() {
323
+ return this.schemaValidator.getAllSchemas(this.gun, this.appName);
324
+ }
325
+
326
+ /**
327
+ * Delete a schema
328
+ */
329
+ async deleteSchema(lens) {
330
+ return this.schemaValidator.deleteSchema(this.gun, this.appName, lens);
331
+ }
332
+
333
+ /**
334
+ * Validate data against a schema
335
+ */
336
+ validate(schema, data) {
337
+ return this.schemaValidator.validate(schema, data);
338
+ }
339
+
340
+ /**
341
+ * Validate data against a lens schema
342
+ */
343
+ async validateData(lens, data) {
344
+ return this.schemaValidator.validateData(this.gun, this.appName, lens, data);
345
+ }
346
+
347
+ /**
348
+ * Clear schema cache
349
+ */
350
+ clearSchemaCache(lens = null) {
351
+ this.schemaValidator.clearCache(lens);
352
+ }
353
+
354
+ // ============================================================================
355
+ // SUBSCRIPTIONS
356
+ // ============================================================================
357
+
101
358
  async subscribe(path, callback, options = {}) {
102
- // Determine if this is a prefix subscription (no key) or single item
359
+ const subscriptionId = ++this.subscriptionCounter;
103
360
  const pathParts = path.split('/');
104
361
  const isPrefix = pathParts.length <= 3;
105
362
 
363
+ const subscription = {
364
+ id: subscriptionId,
365
+ path,
366
+ active: true,
367
+ gunRef: null
368
+ };
369
+
106
370
  if (isPrefix) {
107
- // Subscribe to all items under this prefix
108
371
  const ref = this.gun.get(path);
109
- const handlers = [];
372
+ subscription.gunRef = ref;
373
+
374
+ ref.map().on(async (data, key) => {
375
+ if (!this.subscriptions.get(subscriptionId)?.active) return;
376
+ if (!data || key.startsWith('_') || data._deleted) return;
377
+
378
+ // Parse data
379
+ let parsed = wrapper.parse(data);
380
+ if (!parsed) return;
110
381
 
111
- ref.map().on((data, key) => {
112
- if (data && !key.startsWith('_') && !data._deleted) {
113
- callback(data, key);
382
+ // Resolve references if requested
383
+ if (options.resolveReferences && this.isReference(parsed)) {
384
+ parsed = await this.resolveReference(parsed);
385
+ }
386
+
387
+ if (parsed) {
388
+ callback(parsed, key);
114
389
  }
115
390
  });
391
+ } else {
392
+ const ref = this.gun.get(path);
393
+ subscription.gunRef = ref;
116
394
 
117
- // Store reference for cleanup
118
- handlers.push(ref);
395
+ ref.on(async (data, key) => {
396
+ if (!this.subscriptions.get(subscriptionId)?.active) return;
397
+ if (!data) return;
119
398
 
120
- return {
121
- unsubscribe: () => {
122
- handlers.forEach(h => {
123
- try {
124
- h.off();
125
- } catch (e) {
126
- // Ignore cleanup errors
127
- }
128
- });
129
- },
130
- };
131
- } else {
132
- // Subscribe to single item
133
- const unsubFn = wrapper.subscribe(this.gun, path, callback);
134
- return {
135
- unsubscribe: typeof unsubFn === 'function' ? unsubFn : () => {},
136
- };
399
+ let parsed = wrapper.parse(data);
400
+ if (!parsed || parsed._deleted) return;
401
+
402
+ if (options.resolveReferences && this.isReference(parsed)) {
403
+ parsed = await this.resolveReference(parsed);
404
+ }
405
+
406
+ if (parsed) {
407
+ callback(parsed, key);
408
+ }
409
+ });
137
410
  }
411
+
412
+ this.subscriptions.set(subscriptionId, subscription);
413
+
414
+ return {
415
+ id: subscriptionId,
416
+ unsubscribe: () => {
417
+ const sub = this.subscriptions.get(subscriptionId);
418
+ if (sub) {
419
+ sub.active = false;
420
+ if (sub.gunRef) {
421
+ try {
422
+ sub.gunRef.off();
423
+ } catch (e) {}
424
+ }
425
+ this.subscriptions.delete(subscriptionId);
426
+ }
427
+ }
428
+ };
138
429
  }
139
430
 
431
+ // ============================================================================
432
+ // EXPORT/IMPORT
433
+ // ============================================================================
434
+
140
435
  async exportData(pathPrefix = '') {
141
436
  const records = [];
142
-
143
- // Get the app-level data
144
- const appName = this.config.appName || 'holosphere';
145
- const basePath = pathPrefix || appName;
437
+ const basePath = pathPrefix || this.appName;
146
438
 
147
439
  try {
148
440
  const items = await gunMap(this.gun.get(basePath), 2000);
149
441
 
150
442
  for (const [key, data] of Object.entries(items)) {
151
443
  if (!key.startsWith('_') && data && typeof data === 'object' && !data._deleted) {
152
- // Recursively collect nested data
153
444
  await this._collectRecords(basePath, key, data, records);
154
445
  }
155
446
  }
@@ -164,14 +455,12 @@ export class GunDBBackend extends StorageBackend {
164
455
  const currentPath = `${basePath}/${key}`;
165
456
 
166
457
  if (data.id) {
167
- // This is a leaf data node
168
458
  records.push({
169
459
  path: currentPath,
170
460
  data: this._cleanGunData(data),
171
461
  timestamp: data._meta?.timestamp || Date.now(),
172
462
  });
173
463
  } else {
174
- // This might be a nested structure, explore further
175
464
  try {
176
465
  const nested = await gunMap(this.gun.get(currentPath), 500);
177
466
  for (const [nestedKey, nestedData] of Object.entries(nested)) {
@@ -179,14 +468,11 @@ export class GunDBBackend extends StorageBackend {
179
468
  await this._collectRecords(currentPath, nestedKey, nestedData, records);
180
469
  }
181
470
  }
182
- } catch (e) {
183
- // Ignore nested exploration errors
184
- }
471
+ } catch (e) {}
185
472
  }
186
473
  }
187
474
 
188
475
  _cleanGunData(data) {
189
- // Remove Gun's internal metadata
190
476
  const cleaned = { ...data };
191
477
  delete cleaned['_'];
192
478
  return cleaned;
@@ -208,14 +494,56 @@ export class GunDBBackend extends StorageBackend {
208
494
  return results;
209
495
  }
210
496
 
497
+ // ============================================================================
498
+ // LIFECYCLE
499
+ // ============================================================================
500
+
211
501
  close() {
212
- // Gun doesn't have explicit close, but we can try to clean up
502
+ // 1. Unsubscribe all active subscriptions
503
+ for (const [id, subscription] of this.subscriptions) {
504
+ if (subscription.active) {
505
+ subscription.active = false;
506
+ if (subscription.gunRef) {
507
+ try {
508
+ subscription.gunRef.off();
509
+ } catch (e) {}
510
+ }
511
+ }
512
+ }
513
+ this.subscriptions.clear();
514
+
515
+ // 2. Clear schema cache
516
+ if (this.schemaValidator) {
517
+ this.schemaValidator.clearCache();
518
+ }
519
+
520
+ // 3. Logout auth
521
+ if (this.auth) {
522
+ this.auth.logout();
523
+ }
524
+
525
+ // 4. Clean up Gun connections
213
526
  if (this.gun) {
214
527
  try {
215
- // Attempt to close any connections
528
+ // Clean up mesh connections
529
+ const mesh = this.gun.back('opt.mesh');
530
+ if (mesh) {
531
+ if (mesh.way) {
532
+ Object.values(mesh.way).forEach(conn => {
533
+ if (conn?.wire?.close) conn.wire.close();
534
+ });
535
+ }
536
+ if (mesh.opt?.peers) mesh.opt.peers = {};
537
+ }
538
+
539
+ // Close web server if present
540
+ const server = this.gun.back('opt.web');
541
+ if (server?.close) server.close();
542
+
543
+ // Turn off all listeners
216
544
  this.gun.off();
217
- } catch (e) {
218
- // Ignore cleanup errors
545
+ } catch (error) {
546
+ console.warn('Error during Gun cleanup:', error);
219
547
  }
220
548
  }
221
549
  }
@@ -226,6 +554,9 @@ export class GunDBBackend extends StorageBackend {
226
554
  publicKey: this.publicKey,
227
555
  peers: this.config.peers || [],
228
556
  connected: !!this.gun,
557
+ authenticated: this.isAuthenticated(),
558
+ subscriptionCount: this.subscriptions.size,
559
+ schemaValidationEnabled: this.schemaValidator?.isStrict() || false
229
560
  };
230
561
  }
231
562
  }
@@ -3,7 +3,7 @@
3
3
  * Non-location-specific storage (FR-044, FR-045)
4
4
  */
5
5
 
6
- import { write, read, readAll, update, deleteData, deleteAll } from './nostr-wrapper.js';
6
+ import { write, read, readAll, update, deleteData, deleteAll, subscribe } from './unified-storage.js';
7
7
 
8
8
  /**
9
9
  * Write data to global table
@@ -79,3 +79,30 @@ export async function deleteAllGlobal(client, appname, table) {
79
79
  const path = `${appname}/${table}`;
80
80
  return deleteAll(client, path);
81
81
  }
82
+
83
+ /**
84
+ * Get all data from global table
85
+ * @param {Object} client - Nostr client instance
86
+ * @param {string} appname - Application namespace
87
+ * @param {string} table - Global table name
88
+ * @returns {Promise<Object[]>} Array of data objects
89
+ */
90
+ export async function getAllGlobal(client, appname, table) {
91
+ const path = `${appname}/${table}`;
92
+ return readAll(client, path);
93
+ }
94
+
95
+ /**
96
+ * Subscribe to global table changes
97
+ * @param {Object} client - Nostr client instance
98
+ * @param {string} appname - Application namespace
99
+ * @param {string} table - Global table name
100
+ * @param {string} key - Data key (optional, if not provided subscribes to all)
101
+ * @param {Function} callback - Called on data changes
102
+ * @param {Object} options - Subscription options
103
+ * @returns {Promise<Object>} Subscription object with unsubscribe method
104
+ */
105
+ export async function subscribeGlobal(client, appname, table, key, callback, options = {}) {
106
+ const path = key ? `${appname}/${table}/${key}` : `${appname}/${table}`;
107
+ return subscribe(client, path, callback, options);
108
+ }