holosphere 2.0.0-alpha11 → 2.0.0-alpha13
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/dist/{2019-D2OG2idw.js → 2019-CLMqIAfQ.js} +1722 -1668
- package/dist/{2019-D2OG2idw.js.map → 2019-CLMqIAfQ.js.map} +1 -1
- package/dist/2019-Cp3uYhyY.cjs +8 -0
- package/dist/{2019-EION3wKo.cjs.map → 2019-Cp3uYhyY.cjs.map} +1 -1
- package/dist/browser-D6cNVl0v.cjs +2 -0
- package/dist/{browser-Cq59Ij19.cjs.map → browser-D6cNVl0v.cjs.map} +1 -1
- package/dist/{browser-BSniCNqO.js → browser-nUQt1cnB.js} +2 -2
- package/dist/{browser-BSniCNqO.js.map → browser-nUQt1cnB.js.map} +1 -1
- package/dist/cjs/holosphere.cjs +1 -1
- package/dist/esm/holosphere.js +67 -50
- package/dist/{index-D-jZhliX.js → index-BN_uoxQK.js} +20324 -735
- package/dist/index-BN_uoxQK.js.map +1 -0
- package/dist/{index-Bl6rM1NW.js → index-CoAjtqsD.js} +2 -2
- package/dist/{index-Bl6rM1NW.js.map → index-CoAjtqsD.js.map} +1 -1
- package/dist/{index-Bwg3OzRM.cjs → index-Cp3tI53z.cjs} +3 -3
- package/dist/{index-Bwg3OzRM.cjs.map → index-Cp3tI53z.cjs.map} +1 -1
- package/dist/index-DJjGSwXG.cjs +13 -0
- package/dist/index-DJjGSwXG.cjs.map +1 -0
- package/dist/index-V8EHMYEY.cjs +29 -0
- package/dist/index-V8EHMYEY.cjs.map +1 -0
- package/dist/index-Z5TstN1e.js +11663 -0
- package/dist/index-Z5TstN1e.js.map +1 -0
- package/dist/indexeddb-storage-CZK5A7XH.cjs +2 -0
- package/dist/indexeddb-storage-CZK5A7XH.cjs.map +1 -0
- package/dist/{indexeddb-storage-5eiUNsHC.js → indexeddb-storage-bpA01pAU.js} +39 -2
- package/dist/indexeddb-storage-bpA01pAU.js.map +1 -0
- package/dist/{memory-storage-DMt36uZO.cjs → memory-storage-B1k8Jszd.cjs} +2 -2
- package/dist/{memory-storage-DMt36uZO.cjs.map → memory-storage-B1k8Jszd.cjs.map} +1 -1
- package/dist/{memory-storage-CI-gfmuG.js → memory-storage-BqhmytP_.js} +2 -2
- package/dist/{memory-storage-CI-gfmuG.js.map → memory-storage-BqhmytP_.js.map} +1 -1
- package/docs/FEDERATION.md +474 -0
- package/package.json +3 -1
- package/src/crypto/nostr-utils.js +7 -0
- package/src/crypto/secp256k1.js +104 -38
- package/src/federation/capabilities.js +162 -0
- package/src/federation/card-storage.js +376 -0
- package/src/federation/handshake.js +561 -9
- package/src/federation/hologram.js +194 -57
- package/src/federation/holon-registry.js +187 -0
- package/src/federation/index.js +68 -0
- package/src/federation/registry.js +164 -6
- package/src/federation/request-card.js +373 -0
- package/src/hierarchical/upcast.js +19 -3
- package/src/index.js +209 -75
- package/src/lib/federation-methods.js +527 -5
- package/src/storage/indexeddb-storage.js +41 -0
- package/src/storage/nostr-async.js +14 -5
- package/src/storage/nostr-client.js +471 -155
- package/src/storage/nostr-wrapper.js +6 -3
- package/dist/2019-EION3wKo.cjs +0 -8
- package/dist/_commonjsHelpers-C37NGDzP.cjs +0 -2
- package/dist/_commonjsHelpers-C37NGDzP.cjs.map +0 -1
- package/dist/_commonjsHelpers-CUmg6egw.js +0 -7
- package/dist/_commonjsHelpers-CUmg6egw.js.map +0 -1
- package/dist/browser-Cq59Ij19.cjs +0 -2
- package/dist/index-D-jZhliX.js.map +0 -1
- package/dist/index-Dc6Z8Aob.cjs +0 -18
- package/dist/index-Dc6Z8Aob.cjs.map +0 -1
- package/dist/indexeddb-storage-5eiUNsHC.js.map +0 -1
- package/dist/indexeddb-storage-FNFUVvTJ.cjs +0 -2
- package/dist/indexeddb-storage-FNFUVvTJ.cjs.map +0 -1
- package/dist/secp256k1-CEwJNcfV.js +0 -1890
- package/dist/secp256k1-CEwJNcfV.js.map +0 -1
- package/dist/secp256k1-CiEONUnj.cjs +0 -12
- package/dist/secp256k1-CiEONUnj.cjs.map +0 -1
package/src/index.js
CHANGED
|
@@ -21,6 +21,11 @@ import * as schema from './schema/validator.js';
|
|
|
21
21
|
import { ValidationError } from './schema/validator.js';
|
|
22
22
|
import * as federation from './federation/hologram.js';
|
|
23
23
|
import * as handshake from './federation/handshake.js';
|
|
24
|
+
import * as holonRegistry from './federation/holon-registry.js';
|
|
25
|
+
import * as registry from './federation/registry.js';
|
|
26
|
+
import * as capabilities from './federation/capabilities.js';
|
|
27
|
+
import * as requestCard from './federation/request-card.js';
|
|
28
|
+
import * as cardStorage from './federation/card-storage.js';
|
|
24
29
|
import * as crypto from './crypto/secp256k1.js';
|
|
25
30
|
import * as nostrUtils from './crypto/nostr-utils.js';
|
|
26
31
|
import * as social from './content/social-protocols.js';
|
|
@@ -254,6 +259,51 @@ class HoloSphereBase extends HoloSphereCore {
|
|
|
254
259
|
return spatial.isValidH3(holonId);
|
|
255
260
|
}
|
|
256
261
|
|
|
262
|
+
// === Holon Registry Operations ===
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Register a holon with a public key.
|
|
266
|
+
* Maps a holonId (H3, chatId, concept) to a keypair owner.
|
|
267
|
+
*
|
|
268
|
+
* @param {string} holonId - Holon identifier to register
|
|
269
|
+
* @param {string} publicKey - 64-char hex public key (owner)
|
|
270
|
+
* @param {Object} [options={}] - Registration options
|
|
271
|
+
* @param {string} [options.alias] - Human-readable name
|
|
272
|
+
* @returns {Promise<boolean>} Success indicator
|
|
273
|
+
*/
|
|
274
|
+
async registerHolon(holonId, publicKey, options = {}) {
|
|
275
|
+
return holonRegistry.registerHolon(this.client, this.config.appName, holonId, publicKey, options);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Look up a holon's registration.
|
|
280
|
+
*
|
|
281
|
+
* @param {string} holonId - Holon identifier
|
|
282
|
+
* @returns {Promise<Object|null>} Registry entry { holonId, publicKey, alias } or null
|
|
283
|
+
*/
|
|
284
|
+
async lookupHolon(holonId) {
|
|
285
|
+
return holonRegistry.lookupHolon(this.client, this.config.appName, holonId);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Unregister a holon.
|
|
290
|
+
*
|
|
291
|
+
* @param {string} holonId - Holon identifier
|
|
292
|
+
* @returns {Promise<boolean>} Success indicator
|
|
293
|
+
*/
|
|
294
|
+
async unregisterHolon(holonId) {
|
|
295
|
+
return holonRegistry.unregisterHolon(this.client, this.config.appName, holonId);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* List all registered holons.
|
|
300
|
+
*
|
|
301
|
+
* @returns {Promise<Object[]>} Array of registry entries
|
|
302
|
+
*/
|
|
303
|
+
async getRegisteredHolons() {
|
|
304
|
+
return holonRegistry.listRegisteredHolons(this.client, this.config.appName);
|
|
305
|
+
}
|
|
306
|
+
|
|
257
307
|
// === Data Operations ===
|
|
258
308
|
|
|
259
309
|
/**
|
|
@@ -271,6 +321,7 @@ class HoloSphereBase extends HoloSphereCore {
|
|
|
271
321
|
* @param {boolean} [options.autoPropagate=true] - Whether to propagate to federated holons
|
|
272
322
|
* @param {Object} [options.propagationOptions] - Options for propagation
|
|
273
323
|
* @param {boolean} [options.blocking=false] - If true, wait for relay confirmation before returning
|
|
324
|
+
* @param {string} [options.signingKey] - Private key to sign with (hex format). If not provided, uses holosphere's default key.
|
|
274
325
|
* @returns {Promise<boolean>} True if write succeeded (or queued for optimistic writes)
|
|
275
326
|
* @throws {ValidationError} If holonId, lensName, or data is invalid
|
|
276
327
|
* @throws {AuthorizationError} If capability token is invalid
|
|
@@ -377,7 +428,8 @@ class HoloSphereBase extends HoloSphereCore {
|
|
|
377
428
|
}
|
|
378
429
|
|
|
379
430
|
// Regular write to relay
|
|
380
|
-
|
|
431
|
+
const writeOptions = options.signingKey ? { signingKey: options.signingKey } : {};
|
|
432
|
+
await storage.write(this.client, path, data, writeOptions);
|
|
381
433
|
|
|
382
434
|
const endTime = Date.now();
|
|
383
435
|
const duration = endTime - startTime;
|
|
@@ -486,6 +538,31 @@ class HoloSphereBase extends HoloSphereCore {
|
|
|
486
538
|
return true;
|
|
487
539
|
}
|
|
488
540
|
|
|
541
|
+
/**
|
|
542
|
+
* Resolve holonId to public key.
|
|
543
|
+
* If holonId is already a 64-char hex pubkey, returns it directly.
|
|
544
|
+
* Otherwise, looks up the holon registry.
|
|
545
|
+
*
|
|
546
|
+
* @private
|
|
547
|
+
* @param {string} holonId - Holon identifier
|
|
548
|
+
* @returns {Promise<string|null>} Public key or null if not found
|
|
549
|
+
*/
|
|
550
|
+
async _resolveHolonToPubkey(holonId) {
|
|
551
|
+
return holonRegistry.resolveHolonToPubkey(this.client, this.config.appName, holonId);
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
/**
|
|
555
|
+
* Check if we have a stored capability for a given author and scope.
|
|
556
|
+
*
|
|
557
|
+
* @private
|
|
558
|
+
* @param {string} authorPubKey - Author's public key
|
|
559
|
+
* @param {Object} scope - Scope to check (holonId, lensName, dataId)
|
|
560
|
+
* @returns {Promise<Object|null>} Capability entry or null
|
|
561
|
+
*/
|
|
562
|
+
async _getCapabilityForAuthor(authorPubKey, scope) {
|
|
563
|
+
return registry.getCapabilityForAuthor(this.client, this.config.appName, authorPubKey, scope);
|
|
564
|
+
}
|
|
565
|
+
|
|
489
566
|
/**
|
|
490
567
|
* Recursively resolves holograms (references) to their source data.
|
|
491
568
|
* Handles circular reference detection and local overrides.
|
|
@@ -613,12 +690,98 @@ class HoloSphereBase extends HoloSphereCore {
|
|
|
613
690
|
throw new ValidationError('ValidationError: lensName must be a non-empty string');
|
|
614
691
|
}
|
|
615
692
|
|
|
693
|
+
// OPTIMIZATION: Check local caches first before any network/registry operations
|
|
694
|
+
// This ensures data we just wrote can be read back immediately (important for capability-based writes)
|
|
695
|
+
if (dataId) {
|
|
696
|
+
const earlyPath = storage.buildPath(this.config.appName, holonId, lensName, dataId);
|
|
697
|
+
|
|
698
|
+
// Check delete cache first - if deleted, return null immediately
|
|
699
|
+
if (this._deleteCache.has(earlyPath)) {
|
|
700
|
+
this._log('DEBUG', '🗑️ EARLY CACHE: Deleted item', { path: earlyPath });
|
|
701
|
+
return null;
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
// Check write cache - if this instance wrote data, return it immediately
|
|
705
|
+
const cached = this._writeCache.get(earlyPath);
|
|
706
|
+
if (cached) {
|
|
707
|
+
const cacheAge = Date.now() - cached.timestamp;
|
|
708
|
+
this._log('DEBUG', '⚡ EARLY CACHE HIT: Write cache', {
|
|
709
|
+
path: earlyPath,
|
|
710
|
+
cacheAge: `${cacheAge}ms`
|
|
711
|
+
});
|
|
712
|
+
// Still resolve holograms if needed
|
|
713
|
+
const { resolveHolograms = true } = options;
|
|
714
|
+
if (resolveHolograms && cached.data) {
|
|
715
|
+
return this._resolveHolograms(cached.data);
|
|
716
|
+
}
|
|
717
|
+
return cached.data;
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
// Resolve holonId to public key (if registered)
|
|
722
|
+
const targetPubkey = await this._resolveHolonToPubkey(holonId);
|
|
723
|
+
|
|
724
|
+
// Determine if reading another author's data
|
|
725
|
+
// If holonId can't be resolved, treat as own data (backwards compatible for H3 holons)
|
|
726
|
+
const isOtherAuthor = targetPubkey && targetPubkey !== this.client.publicKey;
|
|
727
|
+
let readOptions = {};
|
|
728
|
+
|
|
729
|
+
// Explicit capability token takes precedence
|
|
616
730
|
const capToken = options.capabilityToken || options.capability;
|
|
617
731
|
if (capToken) {
|
|
618
732
|
const authorized = await this.verifyCapability(capToken, 'read', { holonId, lensName, dataId });
|
|
619
733
|
if (!authorized) {
|
|
620
734
|
throw new AuthorizationError('AuthorizationError: Invalid capability token for read operation', 'read');
|
|
621
735
|
}
|
|
736
|
+
// Use target author for reading
|
|
737
|
+
if (isOtherAuthor) {
|
|
738
|
+
readOptions.authors = [targetPubkey];
|
|
739
|
+
}
|
|
740
|
+
} else if (isOtherAuthor) {
|
|
741
|
+
// Auto-check capability for other author's data
|
|
742
|
+
this._log('DEBUG', '🔍 Looking up capability for federated author', {
|
|
743
|
+
holonId,
|
|
744
|
+
lensName,
|
|
745
|
+
dataId: dataId || '*',
|
|
746
|
+
targetPubkey: targetPubkey?.slice(0, 12) + '...',
|
|
747
|
+
myPubkey: this.client?.publicKey?.slice(0, 12) + '...'
|
|
748
|
+
});
|
|
749
|
+
const capability = await this._getCapabilityForAuthor(targetPubkey, { holonId, lensName, dataId });
|
|
750
|
+
if (!capability) {
|
|
751
|
+
this._log('WARN', '❌ No capability found for federated author - returning empty', {
|
|
752
|
+
holonId,
|
|
753
|
+
lensName,
|
|
754
|
+
targetPubkey: targetPubkey?.slice(0, 12) + '...'
|
|
755
|
+
});
|
|
756
|
+
return dataId ? null : [];
|
|
757
|
+
}
|
|
758
|
+
this._log('DEBUG', '✅ Capability found for federated author', {
|
|
759
|
+
holonId,
|
|
760
|
+
lensName,
|
|
761
|
+
targetPubkey: targetPubkey?.slice(0, 12) + '...'
|
|
762
|
+
});
|
|
763
|
+
readOptions.authors = [targetPubkey];
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
// Include federated authors to see holograms written by federation partners
|
|
767
|
+
// This enables visibility of holograms created during federation
|
|
768
|
+
if (!isOtherAuthor) {
|
|
769
|
+
try {
|
|
770
|
+
const federatedAuthors = await registry.getFederatedAuthorsForScope(
|
|
771
|
+
this.client,
|
|
772
|
+
this.config.appName,
|
|
773
|
+
{ holonId, lensName, dataId },
|
|
774
|
+
'read'
|
|
775
|
+
);
|
|
776
|
+
if (federatedAuthors.length > 0) {
|
|
777
|
+
const partnerPubkeys = federatedAuthors.map(f => f.pubKey);
|
|
778
|
+
// Include self + all federated partners
|
|
779
|
+
readOptions.authors = [this.client.publicKey, ...partnerPubkeys];
|
|
780
|
+
this._log('DEBUG', 'Including federated authors', { count: partnerPubkeys.length });
|
|
781
|
+
}
|
|
782
|
+
} catch (err) {
|
|
783
|
+
this._log('WARN', 'Failed to get federated authors', { error: err.message });
|
|
784
|
+
}
|
|
622
785
|
}
|
|
623
786
|
|
|
624
787
|
const startTime = Date.now();
|
|
@@ -637,6 +800,8 @@ class HoloSphereBase extends HoloSphereCore {
|
|
|
637
800
|
result = null;
|
|
638
801
|
} else {
|
|
639
802
|
// Check write cache for optimistic reads
|
|
803
|
+
// Always check write cache first - if this instance wrote data, it should be able to read it back
|
|
804
|
+
// regardless of holon ownership (important for capability-based writes)
|
|
640
805
|
const cached = this._writeCache.get(path);
|
|
641
806
|
if (cached) {
|
|
642
807
|
const cacheAge = Date.now() - cached.timestamp;
|
|
@@ -648,8 +813,8 @@ class HoloSphereBase extends HoloSphereCore {
|
|
|
648
813
|
});
|
|
649
814
|
result = cached.data;
|
|
650
815
|
} else {
|
|
651
|
-
this._log('DEBUG', '📖 CACHE MISS: Reading from storage', { path });
|
|
652
|
-
result = await storage.read(this.client, path);
|
|
816
|
+
this._log('DEBUG', '📖 CACHE MISS: Reading from storage', { path, authors: readOptions.authors });
|
|
817
|
+
result = await storage.read(this.client, path, readOptions);
|
|
653
818
|
this._log('DEBUG', '💾 STORAGE READ', {
|
|
654
819
|
path,
|
|
655
820
|
source: 'storage',
|
|
@@ -660,11 +825,11 @@ class HoloSphereBase extends HoloSphereCore {
|
|
|
660
825
|
}
|
|
661
826
|
} else {
|
|
662
827
|
const path = storage.buildPath(this.config.appName, holonId, lensName);
|
|
663
|
-
this._log('DEBUG', 'readAll', { holonId, lensName, path });
|
|
828
|
+
this._log('DEBUG', 'readAll', { holonId, lensName, path, authors: readOptions.authors });
|
|
664
829
|
|
|
665
830
|
// For readAll, merge cached writes with storage results and filter deleted
|
|
666
|
-
const storageResult = await storage.readAll(this.client, path);
|
|
667
|
-
result = this._mergeWithWriteCache(storageResult, path);
|
|
831
|
+
const storageResult = await storage.readAll(this.client, path, readOptions);
|
|
832
|
+
result = isOtherAuthor ? storageResult : this._mergeWithWriteCache(storageResult, path);
|
|
668
833
|
this._log('DEBUG', 'readAll result', { count: Array.isArray(result) ? result.length : 0 });
|
|
669
834
|
}
|
|
670
835
|
|
|
@@ -1179,56 +1344,11 @@ class HoloSphereBase extends HoloSphereCore {
|
|
|
1179
1344
|
}
|
|
1180
1345
|
|
|
1181
1346
|
// === Federation Operations ===
|
|
1182
|
-
|
|
1183
|
-
/**
|
|
1184
|
-
* Sets up federation between two holons for a specific lens.
|
|
1185
|
-
* Federation enables data sharing and synchronization between holons.
|
|
1186
|
-
*
|
|
1187
|
-
* @param {string} sourceHolon - Source holon H3 cell ID
|
|
1188
|
-
* @param {string} targetHolon - Target holon H3 cell ID
|
|
1189
|
-
* @param {string} lensName - Name of the lens to federate
|
|
1190
|
-
* @param {Object} [options={}] - Federation options
|
|
1191
|
-
* @param {string} [options.direction='outbound'] - Direction: 'inbound', 'outbound', or 'bidirectional'
|
|
1192
|
-
* @param {string} [options.mode='reference'] - Mode: 'reference' (hologram) or 'copy'
|
|
1193
|
-
* @param {Function} [options.filter] - Filter function to select which data to federate
|
|
1194
|
-
* @returns {Promise<boolean>} True if federation was set up successfully
|
|
1195
|
-
* @throws {Error} If trying to federate a holon with itself or invalid direction
|
|
1196
|
-
*/
|
|
1197
|
-
async federate(sourceHolon, targetHolon, lensName, options = {}) {
|
|
1198
|
-
const { direction = 'outbound', mode = 'reference', filter = null } = options;
|
|
1199
|
-
|
|
1200
|
-
// Validation
|
|
1201
|
-
if (sourceHolon === targetHolon) {
|
|
1202
|
-
throw new Error('Cannot federate a holon with itself');
|
|
1203
|
-
}
|
|
1204
|
-
if (!['inbound', 'outbound', 'bidirectional'].includes(direction)) {
|
|
1205
|
-
throw new Error(`Invalid direction: ${direction}. Must be 'inbound', 'outbound', or 'bidirectional'`);
|
|
1206
|
-
}
|
|
1207
|
-
|
|
1208
|
-
// Store federation config
|
|
1209
|
-
await federation.setupFederation(
|
|
1210
|
-
this.client,
|
|
1211
|
-
this.config.appName,
|
|
1212
|
-
sourceHolon,
|
|
1213
|
-
targetHolon,
|
|
1214
|
-
lensName,
|
|
1215
|
-
options
|
|
1216
|
-
);
|
|
1217
|
-
|
|
1218
|
-
// Actually propagate existing data based on direction
|
|
1219
|
-
if (direction === 'outbound' || direction === 'bidirectional') {
|
|
1220
|
-
await this._propagateExistingData(sourceHolon, targetHolon, lensName, { mode, filter });
|
|
1221
|
-
}
|
|
1222
|
-
if (direction === 'inbound' || direction === 'bidirectional') {
|
|
1223
|
-
await this._propagateExistingData(targetHolon, sourceHolon, lensName, { mode, filter });
|
|
1224
|
-
}
|
|
1225
|
-
|
|
1226
|
-
this._metrics.federations = (this._metrics.federations || 0) + 1;
|
|
1227
|
-
return true;
|
|
1228
|
-
}
|
|
1347
|
+
// Note: federate() and unfederate() methods are provided by withFederationMethods mixin
|
|
1229
1348
|
|
|
1230
1349
|
/**
|
|
1231
1350
|
* Propagates existing data from one holon to another.
|
|
1351
|
+
* UNIFIED MODEL: Passes sourceAuthorPubKey for capability-based federation.
|
|
1232
1352
|
*
|
|
1233
1353
|
* @private
|
|
1234
1354
|
* @param {string} fromHolon - Source holon H3 cell ID
|
|
@@ -1237,10 +1357,11 @@ class HoloSphereBase extends HoloSphereCore {
|
|
|
1237
1357
|
* @param {Object} [options={}] - Propagation options
|
|
1238
1358
|
* @param {string} [options.mode='reference'] - Mode: 'reference' or 'copy'
|
|
1239
1359
|
* @param {Function} [options.filter] - Filter function to select data
|
|
1360
|
+
* @param {string} [options.sourceAuthorPubKey] - Source author's public key (defaults to client.publicKey)
|
|
1240
1361
|
* @returns {Promise<void>}
|
|
1241
1362
|
*/
|
|
1242
1363
|
async _propagateExistingData(fromHolon, toHolon, lensName, options = {}) {
|
|
1243
|
-
const { mode = 'reference', filter = null } = options;
|
|
1364
|
+
const { mode = 'reference', filter = null, sourceAuthorPubKey } = options;
|
|
1244
1365
|
const existingData = await this.read(fromHolon, lensName, null, { resolveHolograms: false });
|
|
1245
1366
|
if (!existingData) return;
|
|
1246
1367
|
|
|
@@ -1250,6 +1371,7 @@ class HoloSphereBase extends HoloSphereCore {
|
|
|
1250
1371
|
if (item.hologram === true) continue;
|
|
1251
1372
|
// Apply filter if provided
|
|
1252
1373
|
if (filter && !filter(item)) continue;
|
|
1374
|
+
// UNIFIED MODEL: Pass sourceAuthorPubKey for capability-based federation
|
|
1253
1375
|
await federation.propagateData(
|
|
1254
1376
|
this.client,
|
|
1255
1377
|
this.config.appName,
|
|
@@ -1257,7 +1379,10 @@ class HoloSphereBase extends HoloSphereCore {
|
|
|
1257
1379
|
fromHolon,
|
|
1258
1380
|
toHolon,
|
|
1259
1381
|
lensName,
|
|
1260
|
-
mode
|
|
1382
|
+
mode,
|
|
1383
|
+
{
|
|
1384
|
+
sourceAuthorPubKey: sourceAuthorPubKey || this.client.publicKey,
|
|
1385
|
+
}
|
|
1261
1386
|
);
|
|
1262
1387
|
}
|
|
1263
1388
|
}
|
|
@@ -1282,25 +1407,6 @@ class HoloSphereBase extends HoloSphereCore {
|
|
|
1282
1407
|
return this._resolveHolograms(data);
|
|
1283
1408
|
}
|
|
1284
1409
|
|
|
1285
|
-
/**
|
|
1286
|
-
* Removes federation between two holons for a specific lens.
|
|
1287
|
-
*
|
|
1288
|
-
* @param {string} sourceHolon - Source holon H3 cell ID
|
|
1289
|
-
* @param {string} targetHolon - Target holon H3 cell ID
|
|
1290
|
-
* @param {string} lensName - Name of the lens
|
|
1291
|
-
* @returns {Promise<boolean>} Always returns true (idempotent operation)
|
|
1292
|
-
*/
|
|
1293
|
-
async unfederate(sourceHolon, targetHolon, lensName) {
|
|
1294
|
-
// Remove federation config for this relationship - idempotent
|
|
1295
|
-
const configPath = storage.buildPath(this.config.appName, sourceHolon, lensName, '_federation');
|
|
1296
|
-
try {
|
|
1297
|
-
await storage.deleteData(this.client, configPath);
|
|
1298
|
-
} catch (e) {
|
|
1299
|
-
// Ignore errors - already unfederated or doesn't exist
|
|
1300
|
-
}
|
|
1301
|
-
return true;
|
|
1302
|
-
}
|
|
1303
|
-
|
|
1304
1410
|
/**
|
|
1305
1411
|
* Updates local override values on a hologram.
|
|
1306
1412
|
*
|
|
@@ -1412,6 +1518,7 @@ class HoloSphereBase extends HoloSphereCore {
|
|
|
1412
1518
|
async propagateData(data, sourceHolon, targetHolon, lensName, options = {}) {
|
|
1413
1519
|
// Extract mode from options, default to 'reference' for hologram creation
|
|
1414
1520
|
const mode = options.mode || 'reference';
|
|
1521
|
+
// UNIFIED MODEL: Pass sourceAuthorPubKey for capability-based federation
|
|
1415
1522
|
return federation.propagateData(
|
|
1416
1523
|
this.client,
|
|
1417
1524
|
this.config.appName,
|
|
@@ -1419,7 +1526,11 @@ class HoloSphereBase extends HoloSphereCore {
|
|
|
1419
1526
|
sourceHolon,
|
|
1420
1527
|
targetHolon,
|
|
1421
1528
|
lensName,
|
|
1422
|
-
mode
|
|
1529
|
+
mode,
|
|
1530
|
+
{
|
|
1531
|
+
sourceAuthorPubKey: options.sourceAuthorPubKey || this.client.publicKey,
|
|
1532
|
+
capability: options.capability,
|
|
1533
|
+
}
|
|
1423
1534
|
);
|
|
1424
1535
|
}
|
|
1425
1536
|
|
|
@@ -2267,10 +2378,33 @@ export {
|
|
|
2267
2378
|
// Re-export types and utilities
|
|
2268
2379
|
export { spatial, storage, schema, federation, handshake, crypto, nostrUtils, social, subscriptions, hierarchical };
|
|
2269
2380
|
|
|
2381
|
+
// Re-export federation submodules
|
|
2382
|
+
export { capabilities, requestCard, cardStorage, holonRegistry, registry };
|
|
2383
|
+
|
|
2270
2384
|
// Re-export specific utilities used in tests
|
|
2271
2385
|
export { matchScope } from './crypto/secp256k1.js';
|
|
2272
2386
|
export { createHologram } from './federation/hologram.js';
|
|
2273
2387
|
|
|
2388
|
+
// Re-export federation card functions
|
|
2389
|
+
export {
|
|
2390
|
+
createFederationCard,
|
|
2391
|
+
getVisibleLenses,
|
|
2392
|
+
getLensConfigForHandshake,
|
|
2393
|
+
toggleLens,
|
|
2394
|
+
toggleCardExpansion,
|
|
2395
|
+
dismissCard,
|
|
2396
|
+
} from './federation/request-card.js';
|
|
2397
|
+
|
|
2398
|
+
// Re-export card storage functions
|
|
2399
|
+
export {
|
|
2400
|
+
saveCard,
|
|
2401
|
+
getCard,
|
|
2402
|
+
getDisplayableCards,
|
|
2403
|
+
dismissRequest,
|
|
2404
|
+
markResponseProcessed,
|
|
2405
|
+
isResponseProcessed,
|
|
2406
|
+
} from './federation/card-storage.js';
|
|
2407
|
+
|
|
2274
2408
|
// Export AI factory function
|
|
2275
2409
|
export { createAIServices } from './ai/index.js';
|
|
2276
2410
|
|