holosphere 1.1.6 → 1.1.7

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/holosphere.js CHANGED
@@ -5,6 +5,7 @@ import SEA from 'gun/sea.js'
5
5
  import Ajv2019 from 'ajv/dist/2019.js'
6
6
  import * as Federation from './federation.js';
7
7
 
8
+ export { federateMessage, getFederatedMessages, updateFederatedMessages, removeNotify } from './federation.js';
8
9
 
9
10
  class HoloSphere {
10
11
  /**
@@ -15,7 +16,7 @@ class HoloSphere {
15
16
  * @param {Gun|null} gunInstance - The Gun instance to use.
16
17
  */
17
18
  constructor(appname, strict = false, openaikey = null, gunInstance = null) {
18
- console.log('HoloSphere v1.1.6');
19
+ console.log('HoloSphere v1.1.7');
19
20
  this.appname = appname
20
21
  this.strict = strict;
21
22
  this.validator = new Ajv2019({
@@ -137,12 +138,16 @@ class HoloSphere {
137
138
  * @param {string} holon - The holon identifier.
138
139
  * @param {string} lens - The lens under which to store the content.
139
140
  * @param {object} data - The data to store.
140
- * @param {string} [password] - Optional password for private space.
141
+ * @param {string} [password] - Optional password for private holon.
142
+ * @param {object} [options] - Additional options
143
+ * @param {boolean} [options.autoPropagate=true] - Whether to automatically propagate to federated holons (default: true)
144
+ * @param {object} [options.propagationOptions] - Options to pass to propagate
145
+ * @param {boolean} [options.propagationOptions.useReferences=true] - Whether to use references instead of duplicating data
141
146
  * @returns {Promise<boolean>} - Returns true if successful, false if there was an error
142
147
  */
143
- async put(holon, lens, data, password = null) {
148
+ async put(holon, lens, data, password = null, options = {}) {
144
149
  if (!holon || !lens || !data) {
145
- throw new Error('put: Missing required parameters');
150
+ throw new Error('put: Missing required parameters:', holon, lens, data );
146
151
  }
147
152
 
148
153
  if (!data.id) {
@@ -237,34 +242,57 @@ class HoloSphere {
237
242
  try {
238
243
  const payload = JSON.stringify(data);
239
244
 
240
- if (password) {
241
- // For private data, use the authenticated user's space
242
- user.get('private').get(lens).get(data.id).put(payload, ack => {
243
- if (ack.err) {
244
- reject(new Error(ack.err));
245
- } else {
246
- this.notifySubscribers({
247
- holon,
248
- lens,
249
- ...data
250
- });
251
- resolve(true);
245
+ const putCallback = async (ack) => {
246
+ if (ack.err) {
247
+ reject(new Error(ack.err));
248
+ } else {
249
+ this.notifySubscribers({
250
+ holon,
251
+ lens,
252
+ ...data
253
+ });
254
+
255
+ // Auto-propagate to federation by default
256
+ const shouldPropagate = options.autoPropagate !== false;
257
+ let propagationResult = null;
258
+
259
+ if (shouldPropagate) {
260
+ try {
261
+ // Default to using references
262
+ const propagationOptions = {
263
+ useReferences: true,
264
+ ...options.propagationOptions
265
+ };
266
+
267
+ propagationResult = await this.propagate(
268
+ holon,
269
+ lens,
270
+ data,
271
+ propagationOptions
272
+ );
273
+
274
+ // Still resolve with true even if propagation had errors
275
+ if (propagationResult.errors > 0) {
276
+ console.warn('Auto-propagation had errors:', propagationResult);
277
+ }
278
+ } catch (propError) {
279
+ console.warn('Error in auto-propagation:', propError);
280
+ }
252
281
  }
253
- });
282
+
283
+ resolve({
284
+ success: true,
285
+ propagationResult
286
+ });
287
+ }
288
+ };
289
+
290
+ if (password) {
291
+ // For private data, use the authenticated user's holon
292
+ user.get('private').get(lens).get(data.id).put(payload, putCallback);
254
293
  } else {
255
294
  // For public data, use the regular path
256
- this.gun.get(this.appname).get(holon).get(lens).get(data.id).put(payload, ack => {
257
- if (ack.err) {
258
- reject(new Error(ack.err));
259
- } else {
260
- this.notifySubscribers({
261
- holon,
262
- lens,
263
- ...data
264
- });
265
- resolve(true);
266
- }
267
- });
295
+ this.gun.get(this.appname).get(holon).get(lens).get(data.id).put(payload, putCallback);
268
296
  }
269
297
  } catch (error) {
270
298
  reject(error);
@@ -281,15 +309,19 @@ class HoloSphere {
281
309
  * @param {string} holon - The holon identifier.
282
310
  * @param {string} lens - The lens from which to retrieve content.
283
311
  * @param {string} key - The specific key to retrieve.
284
- * @param {string} [password] - Optional password for private space.
312
+ * @param {string} [password] - Optional password for private holon.
313
+ * @param {object} [options] - Additional options
314
+ * @param {boolean} [options.resolveReferences=true] - Whether to automatically resolve federation references
285
315
  * @returns {Promise<object|null>} - The retrieved content or null if not found.
286
316
  */
287
- async get(holon, lens, key, password = null) {
317
+ async get(holon, lens, key, password = null, options = {}) {
288
318
  if (!holon || !lens || !key) {
289
319
  console.error('get: Missing required parameters:', { holon, lens, key });
290
320
  return null;
291
321
  }
292
322
 
323
+ const { resolveReferences = true } = options;
324
+
293
325
  // Only check schema in strict mode
294
326
  let schema;
295
327
  if (this.strict) {
@@ -336,6 +368,90 @@ class HoloSphere {
336
368
  try {
337
369
  const parsed = await this.parse(data);
338
370
 
371
+ if (!parsed) {
372
+ resolve(null);
373
+ return;
374
+ }
375
+
376
+ // Check if this is a reference that needs to be resolved
377
+ if (resolveReferences !== false && parsed) {
378
+ // Check if this is a simple reference (id + soul)
379
+ if (parsed.soul) {
380
+ console.log(`Resolving simple reference with soul: ${parsed.soul}`);
381
+ try {
382
+ // For direct soul resolution, we need to parse the soul to get the right path
383
+ const soulParts = parsed.soul.split('/');
384
+ if (soulParts.length >= 4) { // Expected format: appname/holon/lens/key
385
+ const originHolon = soulParts[1];
386
+ const originLens = soulParts[2];
387
+ const originKey = soulParts[3];
388
+
389
+ console.log(`Extracting from soul - holon: ${originHolon}, lens: ${originLens}, key: ${originKey}`);
390
+
391
+ // Get original data using the extracted path components
392
+ const originalData = await this.get(
393
+ originHolon,
394
+ originLens,
395
+ originKey,
396
+ null,
397
+ { resolveReferences: false } // Prevent infinite recursion
398
+ );
399
+
400
+ if (originalData) {
401
+ console.log(`Original data found through soul path resolution:`, originalData);
402
+ resolve({
403
+ ...originalData,
404
+ _federation: {
405
+ isReference: true,
406
+ resolved: true,
407
+ soul: parsed.soul,
408
+ timestamp: Date.now()
409
+ }
410
+ });
411
+ return;
412
+ } else {
413
+ console.warn(`Could not resolve reference: original data not found at extracted path`);
414
+ }
415
+ } else {
416
+ console.warn(`Soul doesn't match expected format: ${parsed.soul}`);
417
+ }
418
+ } catch (error) {
419
+ console.warn(`Error resolving reference by soul: ${error.message}`);
420
+ }
421
+ }
422
+ // Legacy federation reference
423
+ else if (parsed._federation && parsed._federation.isReference) {
424
+ console.log(`Resolving legacy federation reference from ${parsed._federation.origin}`);
425
+ try {
426
+ const reference = parsed._federation;
427
+ const originalData = await this.get(
428
+ reference.origin,
429
+ reference.lens,
430
+ key,
431
+ null,
432
+ { resolveReferences: false } // Prevent infinite recursion
433
+ );
434
+
435
+ if (originalData) {
436
+ return {
437
+ ...originalData,
438
+ _federation: {
439
+ ...reference,
440
+ resolved: true,
441
+ timestamp: Date.now()
442
+ }
443
+ };
444
+ } else {
445
+ console.warn(`Could not resolve legacy reference: original data not found`);
446
+ return parsed; // Return the reference if we can't resolve it
447
+ }
448
+ } catch (error) {
449
+ console.warn(`Error resolving legacy reference: ${error.message}`);
450
+ return parsed;
451
+ }
452
+ }
453
+ }
454
+
339
455
  if (schema) {
340
456
  const valid = this.validator.validate(schema, parsed);
341
457
  if (!valid) {
@@ -355,7 +471,7 @@ class HoloSphere {
355
471
  };
356
472
 
357
473
  if (password) {
358
- // For private data, use the authenticated user's space
474
+ // For private data, use the authenticated user's holon
359
475
  user.get('private').get(lens).get(key).once(handleData);
360
476
  } else {
361
477
  // For public data, use the regular path
@@ -369,31 +485,52 @@ class HoloSphere {
369
485
  }
370
486
 
371
487
  /**
372
- * Propagates data to federated spaces
488
+ * Retrieves a node directly using its soul path
489
+ * @param {string} soul - The soul path of the node
490
+ * @returns {Promise<any>} - The retrieved node or null if not found.
491
+ */
492
+ async getNodeBySoul(soul) {
493
+ if (!soul) {
494
+ throw new Error('getNodeBySoul: Missing soul parameter');
495
+ }
496
+
497
+ console.log(`getNodeBySoul: Accessing soul ${soul}`);
498
+
499
+ return new Promise((resolve) => {
500
+ try {
501
+ const ref = this.getNodeRef(soul);
502
+ ref.once((data) => {
503
+ console.log(`getNodeBySoul: Retrieved data:`, data);
504
+ if (!data) {
505
+ resolve(null);
506
+ return;
507
+ }
508
+ resolve(data); // Return the data directly
509
+ });
510
+ } catch (error) {
511
+ console.error(`getNodeBySoul error:`, error);
512
+ resolve(null);
513
+ }
514
+ });
515
+ }
516
+
517
+ /**
518
+ * Propagates data to federated holons
373
519
  * @param {string} holon - The holon identifier
374
520
  * @param {string} lens - The lens identifier
375
521
  * @param {object} data - The data to propagate
376
522
  * @param {object} [options] - Propagation options
377
523
  * @returns {Promise<object>} - Result with success count and errors
378
524
  */
379
- async propagateToFederation(holon, lens, data, options = {}) {
380
- return Federation.propagateToFederation(this, holon, lens, data, options);
381
- }
382
-
383
- /**
384
- * @private
385
- * @deprecated Use propagateToFederation instead
386
- */
387
- async _propagateToFederation(holon, lens, data) {
388
- console.warn('_propagateToFederation is deprecated, use propagateToFederation instead');
389
- return this.propagateToFederation(holon, lens, data);
525
+ async propagate(holon, lens, data, options = {}) {
526
+ return Federation.propagate(this, holon, lens, data, options);
390
527
  }
391
528
 
392
529
  /**
393
530
  * Retrieves all content from the specified holon and lens.
394
531
  * @param {string} holon - The holon identifier.
395
532
  * @param {string} lens - The lens from which to retrieve content.
396
- * @param {string} [password] - Optional password for private space.
533
+ * @param {string} [password] - Optional password for private holon.
397
534
  * @returns {Promise<Array<object>>} - The retrieved content.
398
535
  */
399
536
  async getAll(holon, lens, password = null) {
@@ -455,7 +592,7 @@ class HoloSphere {
455
592
  };
456
593
 
457
594
  if (password) {
458
- // For private data, use the authenticated user's space
595
+ // For private data, use the authenticated user's holon
459
596
  user.get('private').get(lens).once(handleData);
460
597
  } else {
461
598
  // For public data, use the regular path
@@ -531,7 +668,7 @@ class HoloSphere {
531
668
  * @param {string} holon - The holon identifier.
532
669
  * @param {string} lens - The lens from which to delete the key.
533
670
  * @param {string} key - The specific key to delete.
534
- * @param {string} [password] - Optional password for private space.
671
+ * @param {string} [password] - Optional password for private holon.
535
672
  * @returns {Promise<boolean>} - Returns true if successful
536
673
  */
537
674
  async delete(holon, lens, key, password = null) {
@@ -540,13 +677,13 @@ class HoloSphere {
540
677
  }
541
678
 
542
679
  try {
543
- // Get the appropriate space
680
+ // Get the appropriate holon
544
681
  const user = this.gun.user();
545
682
 
546
- // Delete data from space
683
+ // Delete data from holon
547
684
  return new Promise((resolve, reject) => {
548
685
  if (password) {
549
- // For private data, use the authenticated user's space
686
+ // For private data, use the authenticated user's holon
550
687
  user.get('private').get(lens).get(key).put(null, ack => {
551
688
  if (ack.err) {
552
689
  reject(new Error(ack.err));
@@ -575,7 +712,7 @@ class HoloSphere {
575
712
  * Deletes all keys from a given holon and lens.
576
713
  * @param {string} holon - The holon identifier.
577
714
  * @param {string} lens - The lens from which to delete all keys.
578
- * @param {string} [password] - Optional password for private space.
715
+ * @param {string} [password] - Optional password for private holon.
579
716
  * @returns {Promise<boolean>} - Returns true if successful
580
717
  */
581
718
  async deleteAll(holon, lens, password = null) {
@@ -585,7 +722,7 @@ class HoloSphere {
585
722
  }
586
723
 
587
724
  try {
588
- // Get the appropriate space
725
+ // Get the appropriate holon
589
726
  const user = this.gun.user();
590
727
 
591
728
  return new Promise((resolve) => {
@@ -752,7 +889,7 @@ class HoloSphere {
752
889
  * Stores data in a global (non-holon-specific) table.
753
890
  * @param {string} tableName - The table name to store data in.
754
891
  * @param {object} data - The data to store. If it has an 'id' field, it will be used as the key.
755
- * @param {string} [password] - Optional password for private space.
892
+ * @param {string} [password] - Optional password for private holon.
756
893
  * @returns {Promise<void>}
757
894
  */
758
895
  async putGlobal(tableName, data, password = null) {
@@ -825,7 +962,7 @@ class HoloSphere {
825
962
  const payload = JSON.stringify(data);
826
963
 
827
964
  if (password) {
828
- // For private data, use the authenticated user's space
965
+ // For private data, use the authenticated user's holon
829
966
  const path = user.get('private').get(tableName);
830
967
 
831
968
  if (data.id) {
@@ -878,7 +1015,7 @@ class HoloSphere {
878
1015
  * Retrieves a specific key from a global table.
879
1016
  * @param {string} tableName - The table name to retrieve from.
880
1017
  * @param {string} key - The key to retrieve.
881
- * @param {string} [password] - Optional password for private space.
1018
+ * @param {string} [password] - Optional password for private holon.
882
1019
  * @returns {Promise<object|null>} - The parsed data for the key or null if not found.
883
1020
  */
884
1021
  async getGlobal(tableName, key, password = null) {
@@ -942,7 +1079,7 @@ class HoloSphere {
942
1079
  };
943
1080
 
944
1081
  if (password) {
945
- // For private data, use the authenticated user's space
1082
+ // For private data, use the authenticated user's holon
946
1083
  user.get('private').get(tableName).get(key).once(handleData);
947
1084
  } else {
948
1085
  // For public data, use the regular path
@@ -958,7 +1095,7 @@ class HoloSphere {
958
1095
  /**
959
1096
  * Retrieves all data from a global table.
960
1097
  * @param {string} tableName - The table name to retrieve data from.
961
- * @param {string} [password] - Optional password for private space.
1098
+ * @param {string} [password] - Optional password for private holon.
962
1099
  * @returns {Promise<Array<object>>} - The parsed data from the table as an array.
963
1100
  */
964
1101
  async getAllGlobal(tableName, password = null) {
@@ -967,7 +1104,7 @@ class HoloSphere {
967
1104
  }
968
1105
 
969
1106
  try {
970
- // Get the appropriate space
1107
+ // Get the appropriate holon
971
1108
  const user = this.gun.user();
972
1109
 
973
1110
  return new Promise((resolve) => {
@@ -1020,7 +1157,7 @@ class HoloSphere {
1020
1157
  };
1021
1158
 
1022
1159
  if (password) {
1023
- // For private data, use the authenticated user's space
1160
+ // For private data, use the authenticated user's holon
1024
1161
  user.get('private').get(tableName).once(handleData);
1025
1162
  } else {
1026
1163
  // For public data, use the regular path
@@ -1037,7 +1174,7 @@ class HoloSphere {
1037
1174
  * Deletes a specific key from a global table.
1038
1175
  * @param {string} tableName - The table name to delete from.
1039
1176
  * @param {string} key - The key to delete.
1040
- * @param {string} [password] - Optional password for private space.
1177
+ * @param {string} [password] - Optional password for private holon.
1041
1178
  * @returns {Promise<boolean>}
1042
1179
  */
1043
1180
  async deleteGlobal(tableName, key, password = null) {
@@ -1046,12 +1183,12 @@ class HoloSphere {
1046
1183
  }
1047
1184
 
1048
1185
  try {
1049
- // Get the appropriate space
1186
+ // Get the appropriate holon
1050
1187
  const user = this.gun.user();
1051
1188
 
1052
1189
  return new Promise((resolve, reject) => {
1053
1190
  if (password) {
1054
- // For private data, use the authenticated user's space
1191
+ // For private data, use the authenticated user's holon
1055
1192
  user.get('private').get(tableName).get(key).put(null, ack => {
1056
1193
  if (ack.err) {
1057
1194
  reject(new Error(ack.err));
@@ -1079,7 +1216,7 @@ class HoloSphere {
1079
1216
  /**
1080
1217
  * Deletes an entire global table.
1081
1218
  * @param {string} tableName - The table name to delete.
1082
- * @param {string} [password] - Optional password for private space.
1219
+ * @param {string} [password] - Optional password for private holon.
1083
1220
  * @returns {Promise<boolean>}
1084
1221
  */
1085
1222
  async deleteAllGlobal(tableName, password = null) {
@@ -1088,7 +1225,7 @@ class HoloSphere {
1088
1225
  }
1089
1226
 
1090
1227
  try {
1091
- // Get the appropriate space
1228
+ // Get the appropriate holon
1092
1229
  const user = this.gun.user();
1093
1230
 
1094
1231
  return new Promise((resolve, reject) => {
@@ -1154,7 +1291,7 @@ class HoloSphere {
1154
1291
  * @param {string} lens - The lens to compute
1155
1292
  * @param {object} options - Computation options
1156
1293
  * @param {number} [maxLevels=15] - Maximum levels to compute up
1157
- * @param {string} [password] - Optional password for private spaces
1294
+ * @param {string} [password] - Optional password for private holons
1158
1295
  */
1159
1296
  async computeHierarchy(holon, lens, options, maxLevels = 15, password = null) {
1160
1297
  let currentHolon = holon;
@@ -1187,7 +1324,7 @@ class HoloSphere {
1187
1324
  * @param {string} options.operation - The operation to perform ('summarize', 'aggregate', 'concatenate')
1188
1325
  * @param {string[]} [options.fields] - Fields to perform operation on
1189
1326
  * @param {string} [options.targetField] - Field to store the result in
1190
- * @param {string} [password] - Optional password for private spaces
1327
+ * @param {string} [password] - Optional password for private holons
1191
1328
  * @throws {Error} If parameters are invalid or missing
1192
1329
  */
1193
1330
  async compute(holon, lens, options, password = null) {
@@ -1354,27 +1491,52 @@ class HoloSphere {
1354
1491
  }
1355
1492
 
1356
1493
  /**
1357
- * Upcasts content to parent holonagons recursively.
1494
+ * Upcasts content to parent holonagons recursively using federation and soul references.
1495
+ * This is the modern implementation that uses federation references instead of duplicating data.
1358
1496
  * @param {string} holon - The current holon identifier.
1359
1497
  * @param {string} lens - The lens under which to upcast.
1360
1498
  * @param {object} content - The content to upcast.
1361
- * @returns {Promise<object>} - The upcasted content.
1499
+ * @param {number} [maxLevels=15] - Maximum levels to upcast.
1500
+ * @returns {Promise<object>} - The original content.
1362
1501
  */
1363
- async upcast(holon, lens, content) {
1364
- let res = h3.getResolution(holon)
1365
- if (res == 0) {
1366
- await this.put(holon, lens, content)
1367
- return content
1502
+ async upcast(holon, lens, content, maxLevels = 15) {
1503
+ // Store the actual content at the original resolution
1504
+ await this.put(holon, lens, content);
1505
+
1506
+ let res = h3.getResolution(holon);
1507
+
1508
+ // If already at the highest level (res 0) or reached max levels, we're done
1509
+ if (res === 0 || maxLevels <= 0) {
1510
+ return content;
1368
1511
  }
1369
- else {
1370
- console.log('Upcasting ', holon, lens, content, res)
1371
- await this.put(holon, lens, content)
1372
- let parent = h3.cellToParent(holon, res - 1)
1373
- return this.upcast(parent, lens, content)
1512
+
1513
+ // Get the parent cell
1514
+ let parent = h3.cellToParent(holon, res - 1);
1515
+
1516
+ // Create federation relationship if it doesn't exist
1517
+ await this.federate(holon, parent);
1518
+
1519
+ // Create a soul reference to store in the parent
1520
+ const soul = `${this.appname}/${holon}/${lens}/${content.id}`;
1521
+ const reference = {
1522
+ id: content.id,
1523
+ soul: soul
1524
+ };
1525
+
1526
+ // Store the reference in the parent cell
1527
+ // We use { autoPropagate: false } to prevent circular propagation
1528
+ await this.put(parent, lens, reference, null, {
1529
+ autoPropagate: false
1530
+ });
1531
+
1532
+ // Continue upcasting with the parent
1533
+ if (res > 1 && maxLevels > 1) {
1534
+ return this.upcast(parent, lens, reference, maxLevels - 1);
1374
1535
  }
1536
+
1537
+ return content;
1375
1538
  }
1376
1539
 
1377
-
1378
1540
  /**
1379
1541
  * Updates the parent holon with a new report.
1380
1542
  * @param {string} id - The child holon identifier.
@@ -1534,65 +1696,131 @@ class HoloSphere {
1534
1696
  // ================================ FEDERATION FUNCTIONS ================================
1535
1697
 
1536
1698
  /**
1537
- * Creates a federation relationship between two spaces
1538
- * @param {string} spaceId1 - The first space ID
1539
- * @param {string} spaceId2 - The second space ID
1540
- * @param {string} password1 - Password for the first space
1541
- * @param {string} [password2] - Optional password for the second space
1699
+ * Creates a federation relationship between two holons
1700
+ * @param {string} holonId1 - The first holon ID
1701
+ * @param {string} holonId2 - The second holon ID
1702
+ * @param {string} password1 - Password for the first holon
1703
+ * @param {string} [password2] - Optional password for the second holon
1704
+ * @param {boolean} [bidirectional=true] - Whether to set up bidirectional notifications automatically
1542
1705
  * @returns {Promise<boolean>} - True if federation was created successfully
1543
1706
  */
1544
- async federate(spaceId1, spaceId2, password1, password2 = null) {
1545
- return Federation.federate(this, spaceId1, spaceId2, password1, password2);
1707
+ async federate(holonId1, holonId2, password1, password2 = null, bidirectional = true) {
1708
+ return Federation.federate(this, holonId1, holonId2, password1, password2, bidirectional);
1546
1709
  }
1547
1710
 
1548
1711
  /**
1549
- * Subscribes to federation notifications for a space
1550
- * @param {string} spaceId - The space ID to subscribe to
1551
- * @param {string} password - Password for the space
1712
+ * Subscribes to federation notifications for a holon
1713
+ * @param {string} holonId - The holon ID to subscribe to
1714
+ * @param {string} password - Password for the holon
1552
1715
  * @param {function} callback - The callback to execute on notifications
1553
1716
  * @param {object} [options] - Subscription options
1554
1717
  * @param {string[]} [options.lenses] - Specific lenses to subscribe to (default: all)
1555
1718
  * @param {number} [options.throttle] - Throttle notifications in ms (default: 0)
1556
1719
  * @returns {Promise<object>} - Subscription object with unsubscribe() method
1557
1720
  */
1558
- async subscribeFederation(spaceId, password, callback, options = {}) {
1559
- return Federation.subscribeFederation(this, spaceId, password, callback, options);
1721
+ async subscribeFederation(holonId, password, callback, options = {}) {
1722
+ return Federation.subscribeFederation(this, holonId, password, callback, options);
1560
1723
  }
1561
1724
 
1562
1725
  /**
1563
- * Gets federation info for a space
1564
- * @param {string} spaceId - The space ID
1565
- * @param {string} [password] - Optional password for the space
1726
+ * Gets federation info for a holon
1727
+ * @param {string} holonId - The holon ID
1728
+ * @param {string} [password] - Optional password for the holon
1566
1729
  * @returns {Promise<object|null>} - Federation info or null if not found
1567
1730
  */
1568
- async getFederation(spaceId, password = null) {
1569
- return Federation.getFederation(this, spaceId, password);
1731
+ async getFederation(holonId, password = null) {
1732
+ return Federation.getFederation(this, holonId, password);
1570
1733
  }
1571
1734
 
1572
1735
  /**
1573
- * Removes a federation relationship between spaces
1574
- * @param {string} spaceId1 - The first space ID
1575
- * @param {string} spaceId2 - The second space ID
1576
- * @param {string} password1 - Password for the first space
1577
- * @param {string} [password2] - Optional password for the second space
1736
+ * Removes a federation relationship between holons
1737
+ * @param {string} holonId1 - The first holon ID
1738
+ * @param {string} holonId2 - The second holon ID
1739
+ * @param {string} password1 - Password for the first holon
1740
+ * @param {string} [password2] - Optional password for the second holon
1578
1741
  * @returns {Promise<boolean>} - True if federation was removed successfully
1579
1742
  */
1580
- async unfederate(spaceId1, spaceId2, password1, password2 = null) {
1581
- return Federation.unfederate(this, spaceId1, spaceId2, password1, password2);
1743
+ async unfederate(holonId1, holonId2, password1, password2 = null) {
1744
+ return await Federation.unfederate(this, holonId1, holonId2, password1, password2);
1582
1745
  }
1583
1746
 
1584
1747
  /**
1585
- * Gets data from a holon and lens, including data from federated spaces with optional aggregation
1586
- * @param {string} holon - The holon identifier
1587
- * @param {string} lens - The lens identifier
1588
- * @param {object} options - Options for data retrieval and aggregation
1589
- * @param {string} [password] - Optional password for accessing private data
1590
- * @returns {Promise<Array>} - Combined array of local and federated data
1748
+ * Removes a notification relationship between two spaces
1749
+ * This removes spaceId2 from the notify list of spaceId1
1750
+ *
1751
+ * @param {string} holonId1 - The space to modify (remove from its notify list)
1752
+ * @param {string} holonId2 - The space to be removed from notifications
1753
+ * @param {string} [password1] - Optional password for the first space
1754
+ * @returns {Promise<boolean>} - True if notification was removed successfully
1755
+ */
1756
+ async removeNotify(holonId1, holonId2, password1 = null) {
1757
+ console.log(`HoloSphere.removeNotify called: ${holonId1}, ${holonId2}`);
1758
+ try {
1759
+ const result = await Federation.removeNotify(this, holonId1, holonId2, password1);
1760
+ console.log(`HoloSphere.removeNotify completed successfully: ${result}`);
1761
+ return result;
1762
+ } catch (error) {
1763
+ console.error(`HoloSphere.removeNotify failed:`, error);
1764
+ throw error;
1765
+ }
1766
+ }
1767
+
1768
+ /**
1769
+ * Get and aggregate data from federated holons
1770
+ * @param {string} holon The holon name
1771
+ * @param {string} lens The lens name
1772
+ * @param {Object} options Options for retrieval and aggregation
1773
+ * @returns {Promise<Array>} Combined array of local and federated data
1591
1774
  */
1592
- async getFederated(holon, lens, options = {}, password = null) {
1593
- return Federation.getFederated(this, holon, lens, options, password);
1775
+ async getFederated(holon, lens, options = {}) {
1776
+ return Federation.getFederated(this, holon, lens, options);
1594
1777
  }
1595
1778
 
1779
+ /**
1780
+ * Tracks a federated message across different chats
1781
+ * @param {string} originalChatId - The ID of the original chat
1782
+ * @param {string} messageId - The ID of the original message
1783
+ * @param {string} federatedChatId - The ID of the federated chat
1784
+ * @param {string} federatedMessageId - The ID of the message in the federated chat
1785
+ * @param {string} type - The type of message (e.g., 'quest', 'announcement')
1786
+ * @returns {Promise<void>}
1787
+ */
1788
+ async federateMessage(originalChatId, messageId, federatedChatId, federatedMessageId, type = 'generic') {
1789
+ return Federation.federateMessage(this, originalChatId, messageId, federatedChatId, federatedMessageId, type);
1790
+ }
1791
+
1792
+ /**
1793
+ * Gets all federated messages for a given original message
1794
+ * @param {string} originalChatId - The ID of the original chat
1795
+ * @param {string} messageId - The ID of the original message
1796
+ * @returns {Promise<Object|null>} The tracking information for the message
1797
+ */
1798
+ async getFederatedMessages(originalChatId, messageId) {
1799
+ return Federation.getFederatedMessages(this, originalChatId, messageId);
1800
+ }
1801
+
1802
+ /**
1803
+ * Updates a federated message across all federated chats
1804
+ * @param {string} originalChatId - The ID of the original chat
1805
+ * @param {string} messageId - The ID of the original message
1806
+ * @param {Function} updateCallback - Function to update the message in each chat
1807
+ * @returns {Promise<void>}
1808
+ */
1809
+ async updateFederatedMessages(originalChatId, messageId, updateCallback) {
1810
+ return Federation.updateFederatedMessages(this, originalChatId, messageId, updateCallback);
1811
+ }
1812
+
1813
+ /**
1814
+ * Resets the federation settings for a holon
1815
+ * @param {string} holonId - The holon ID
1816
+ * @param {string} [password] - Optional password for the holon
1817
+ * @returns {Promise<boolean>} - True if federation was reset successfully
1818
+ */
1819
+ async resetFederation(holonId, password = null) {
1820
+ return Federation.resetFederation(this, holonId, password);
1821
+ }
1822
+
1823
+ // ================================ END FEDERATION FUNCTIONS ================================
1596
1824
  /**
1597
1825
  * Closes the HoloSphere instance and cleans up resources.
1598
1826
  * @returns {Promise<void>}
@@ -1669,26 +1897,15 @@ class HoloSphere {
1669
1897
  }
1670
1898
  }
1671
1899
 
1672
- /**
1673
- * Gets the name of a chat/space
1674
- * @param {string} spaceId - The space ID
1675
- * @param {string} [password] - Optional password for the space
1676
- * @returns {Promise<string>} - The space name or the ID if not found
1677
- */
1678
- async getChatName(spaceId, password = null) {
1679
- const spaceInfo = await this.getGlobal('spaces', spaceId, password);
1680
- return spaceInfo?.name || spaceId;
1681
- }
1682
-
1683
1900
  /**
1684
1901
  * Creates a namespaced username for Gun authentication
1685
1902
  * @private
1686
- * @param {string} spaceId - The space ID
1903
+ * @param {string} holonId - The holon ID
1687
1904
  * @returns {string} - Namespaced username
1688
1905
  */
1689
- userName(spaceId) {
1690
- if (!spaceId) return null;
1691
- return `${this.appname}:${spaceId}`;
1906
+ userName(holonId) {
1907
+ if (!holonId) return null;
1908
+ return `${this.appname}:${holonId}`;
1692
1909
  }
1693
1910
  }
1694
1911