holosphere 2.0.0-alpha0 → 2.0.0-alpha10
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/CHANGELOG.md +473 -0
- package/FEATURES.md +431 -0
- package/LICENSE +29 -42
- package/LICENSE-AGPL.md +180 -0
- package/README.md +97 -16
- package/dist/2019-D2OG2idw.js +6680 -0
- package/dist/2019-D2OG2idw.js.map +1 -0
- package/dist/2019-EION3wKo.cjs +8 -0
- package/dist/2019-EION3wKo.cjs.map +1 -0
- package/dist/_commonjsHelpers-C37NGDzP.cjs +2 -0
- package/dist/_commonjsHelpers-C37NGDzP.cjs.map +1 -0
- package/dist/_commonjsHelpers-CUmg6egw.js +7 -0
- package/dist/_commonjsHelpers-CUmg6egw.js.map +1 -0
- package/dist/browser-BSniCNqO.js +3058 -0
- package/dist/browser-BSniCNqO.js.map +1 -0
- package/dist/browser-Cq59Ij19.cjs +2 -0
- package/dist/browser-Cq59Ij19.cjs.map +1 -0
- package/dist/cdn/holosphere.min.js +55 -0
- package/dist/cdn/holosphere.min.js.map +1 -0
- package/dist/cjs/holosphere.cjs +2 -0
- package/dist/cjs/holosphere.cjs.map +1 -0
- package/dist/esm/holosphere.js +53 -0
- package/dist/esm/holosphere.js.map +1 -0
- package/dist/index-DDGt_V9o.cjs +12 -0
- package/dist/index-DDGt_V9o.cjs.map +1 -0
- package/dist/index-DJXftyvB.js +39841 -0
- package/dist/index-DJXftyvB.js.map +1 -0
- package/dist/index-DMbdcMtK.cjs +18 -0
- package/dist/index-DMbdcMtK.cjs.map +1 -0
- package/dist/index-DeZ1xz_s.js +15104 -0
- package/dist/index-DeZ1xz_s.js.map +1 -0
- package/dist/indexeddb-storage-BFt6hMeF.js +176 -0
- package/dist/indexeddb-storage-BFt6hMeF.js.map +1 -0
- package/dist/indexeddb-storage-BK5tv4Sh.cjs +2 -0
- package/dist/indexeddb-storage-BK5tv4Sh.cjs.map +1 -0
- package/dist/memory-storage-C9HuoL2E.js +91 -0
- package/dist/memory-storage-C9HuoL2E.js.map +1 -0
- package/dist/memory-storage-Dao7jfYG.cjs +2 -0
- package/dist/memory-storage-Dao7jfYG.cjs.map +1 -0
- package/dist/secp256k1-BbKzbLtD.cjs +12 -0
- package/dist/secp256k1-BbKzbLtD.cjs.map +1 -0
- package/dist/secp256k1-CreY7Pcl.js +1890 -0
- package/dist/secp256k1-CreY7Pcl.js.map +1 -0
- package/docs/CONTRACTS.md +797 -0
- package/docs/FOSDEM_PROPOSAL.md +388 -0
- package/docs/LOCALFIRST.md +266 -0
- package/docs/api/ai_aggregation.js.html +333 -0
- package/docs/api/ai_breakdown.js.html +524 -0
- package/docs/api/ai_classifier.js.html +231 -0
- package/docs/api/ai_council.js.html +246 -0
- package/docs/api/ai_embeddings.js.html +304 -0
- package/docs/api/ai_federation-ai.js.html +338 -0
- package/docs/api/ai_h3-ai.js.html +970 -0
- package/docs/api/ai_index.js.html +124 -0
- package/docs/api/ai_json-ops.js.html +241 -0
- package/docs/api/ai_llm-service.js.html +239 -0
- package/docs/api/ai_nl-query.js.html +236 -0
- package/docs/api/ai_relationships.js.html +367 -0
- package/docs/api/ai_schema-extractor.js.html +235 -0
- package/docs/api/ai_spatial.js.html +307 -0
- package/docs/api/ai_tts.js.html +214 -0
- package/docs/api/content_social-protocols.js.html +180 -0
- package/docs/api/core_holosphere.js.html +757 -0
- package/docs/api/crypto_nostr-utils.js.html +306 -0
- package/docs/api/crypto_secp256k1.js.html +267 -0
- package/docs/api/data/search.json +1 -0
- package/docs/api/federation_discovery.js.html +337 -0
- package/docs/api/federation_handshake.js.html +478 -0
- package/docs/api/federation_hologram.js.html +1053 -0
- package/docs/api/federation_registry.js.html +389 -0
- package/docs/api/fonts/Inconsolata-Regular.ttf +0 -0
- package/docs/api/fonts/OpenSans-Regular.ttf +0 -0
- package/docs/api/fonts/WorkSans-Bold.ttf +0 -0
- package/docs/api/global.html +3 -0
- package/docs/api/hierarchical_upcast.js.html +128 -0
- package/docs/api/index.html +265 -0
- package/docs/api/index.js.html +1868 -0
- package/docs/api/lib_ai-methods.js.html +660 -0
- package/docs/api/lib_contract-methods.js.html +445 -0
- package/docs/api/lib_errors.js.html +56 -0
- package/docs/api/lib_federation-methods.js.html +348 -0
- package/docs/api/lib_index.js.html +33 -0
- package/docs/api/module-ai.html +5 -0
- package/docs/api/module-ai_aggregation-SmartAggregation.html +6 -0
- package/docs/api/module-ai_aggregation.SmartAggregation.html +3 -0
- package/docs/api/module-ai_aggregation.html +3 -0
- package/docs/api/module-ai_breakdown-TaskBreakdown.html +5 -0
- package/docs/api/module-ai_breakdown.TaskBreakdown.html +3 -0
- package/docs/api/module-ai_breakdown.html +3 -0
- package/docs/api/module-ai_classifier-Classifier.html +6 -0
- package/docs/api/module-ai_classifier.Classifier.html +3 -0
- package/docs/api/module-ai_classifier.html +3 -0
- package/docs/api/module-ai_council-Council.html +6 -0
- package/docs/api/module-ai_council.Council.html +3 -0
- package/docs/api/module-ai_council.html +3 -0
- package/docs/api/module-ai_embeddings-Embeddings.html +5 -0
- package/docs/api/module-ai_embeddings.Embeddings.html +3 -0
- package/docs/api/module-ai_embeddings.html +3 -0
- package/docs/api/module-ai_federation-ai-FederationAdvisor.html +6 -0
- package/docs/api/module-ai_federation-ai.FederationAdvisor.html +3 -0
- package/docs/api/module-ai_federation-ai.html +3 -0
- package/docs/api/module-ai_h3-ai-H3AI.html +6 -0
- package/docs/api/module-ai_h3-ai.H3AI.html +3 -0
- package/docs/api/module-ai_h3-ai.html +3 -0
- package/docs/api/module-ai_json-ops-JSONOps.html +5 -0
- package/docs/api/module-ai_json-ops.JSONOps.html +3 -0
- package/docs/api/module-ai_json-ops.html +3 -0
- package/docs/api/module-ai_llm-service-LLMService.html +5 -0
- package/docs/api/module-ai_llm-service.LLMService.html +3 -0
- package/docs/api/module-ai_llm-service.html +3 -0
- package/docs/api/module-ai_nl-query-NLQuery.html +5 -0
- package/docs/api/module-ai_nl-query.NLQuery.html +3 -0
- package/docs/api/module-ai_nl-query.html +3 -0
- package/docs/api/module-ai_relationships-RelationshipDiscovery.html +6 -0
- package/docs/api/module-ai_relationships.RelationshipDiscovery.html +3 -0
- package/docs/api/module-ai_relationships.html +3 -0
- package/docs/api/module-ai_schema-extractor-SchemaExtractor.html +5 -0
- package/docs/api/module-ai_schema-extractor.SchemaExtractor.html +3 -0
- package/docs/api/module-ai_schema-extractor.html +3 -0
- package/docs/api/module-ai_spatial-SpatialAnalysis.html +6 -0
- package/docs/api/module-ai_spatial.SpatialAnalysis.html +3 -0
- package/docs/api/module-ai_spatial.html +3 -0
- package/docs/api/module-ai_tts-TTS.html +5 -0
- package/docs/api/module-ai_tts.TTS.html +3 -0
- package/docs/api/module-ai_tts.html +3 -0
- package/docs/api/module-content_social-protocols.html +3 -0
- package/docs/api/module-core_holosphere-HoloSphere.html +6 -0
- package/docs/api/module-core_holosphere.HoloSphere.html +3 -0
- package/docs/api/module-core_holosphere.html +3 -0
- package/docs/api/module-crypto_nostr-utils.html +3 -0
- package/docs/api/module-crypto_secp256k1.html +3 -0
- package/docs/api/module-federation_hologram.html +3 -0
- package/docs/api/module-hierarchical_upcast.html +3 -0
- package/docs/api/module-holosphere-HoloSphereBase.html +3 -0
- package/docs/api/module-holosphere.html +3 -0
- package/docs/api/module-lib_ai-methods.html +3 -0
- package/docs/api/module-lib_contract-methods.html +3 -0
- package/docs/api/module-lib_errors-AuthorizationError.html +3 -0
- package/docs/api/module-lib_errors-ValidationError.html +3 -0
- package/docs/api/module-lib_errors.AuthorizationError.html +3 -0
- package/docs/api/module-lib_errors.ValidationError.html +3 -0
- package/docs/api/module-lib_errors.html +3 -0
- package/docs/api/module-lib_federation-methods.html +3 -0
- package/docs/api/module-lib_index.html +3 -0
- package/docs/api/module-schema_validator-ValidationError.html +3 -0
- package/docs/api/module-schema_validator.ValidationError.html +3 -0
- package/docs/api/module-schema_validator.html +3 -0
- package/docs/api/module-spatial_h3-operations.html +4 -0
- package/docs/api/module-storage_backend-factory.BackendFactory.html +3 -0
- package/docs/api/module-storage_backend-factory.html +3 -0
- package/docs/api/module-storage_backend-interface-StorageBackend.html +3 -0
- package/docs/api/module-storage_backend-interface.StorageBackend.html +3 -0
- package/docs/api/module-storage_backend-interface.html +3 -0
- package/docs/api/module-storage_backends_activitypub-backend-ActivityPubBackend.html +7 -0
- package/docs/api/module-storage_backends_activitypub-backend.ActivityPubBackend.html +3 -0
- package/docs/api/module-storage_backends_activitypub-backend.html +3 -0
- package/docs/api/module-storage_backends_activitypub_server-ActivityPubServer.html +8 -0
- package/docs/api/module-storage_backends_activitypub_server.ActivityPubServer.html +3 -0
- package/docs/api/module-storage_backends_activitypub_server.html +3 -0
- package/docs/api/module-storage_backends_gundb-backend-GunDBBackend.html +7 -0
- package/docs/api/module-storage_backends_gundb-backend.GunDBBackend.html +3 -0
- package/docs/api/module-storage_backends_gundb-backend.html +3 -0
- package/docs/api/module-storage_backends_nostr-backend-NostrBackend.html +8 -0
- package/docs/api/module-storage_backends_nostr-backend.NostrBackend.html +3 -0
- package/docs/api/module-storage_backends_nostr-backend.html +3 -0
- package/docs/api/module-storage_filesystem-storage-FileSystemStorage.html +5 -0
- package/docs/api/module-storage_filesystem-storage-browser-FileSystemStorage.html +3 -0
- package/docs/api/module-storage_filesystem-storage-browser.FileSystemStorage.html +3 -0
- package/docs/api/module-storage_filesystem-storage-browser.html +3 -0
- package/docs/api/module-storage_filesystem-storage.FileSystemStorage.html +3 -0
- package/docs/api/module-storage_filesystem-storage.html +3 -0
- package/docs/api/module-storage_global-tables.html +3 -0
- package/docs/api/module-storage_gun-async.html +3 -0
- package/docs/api/module-storage_gun-auth-GunAuth.html +5 -0
- package/docs/api/module-storage_gun-auth.GunAuth.html +3 -0
- package/docs/api/module-storage_gun-auth.html +3 -0
- package/docs/api/module-storage_gun-federation.html +3 -0
- package/docs/api/module-storage_gun-references-GunReferenceHandler.html +5 -0
- package/docs/api/module-storage_gun-references.GunReferenceHandler.html +3 -0
- package/docs/api/module-storage_gun-references.html +3 -0
- package/docs/api/module-storage_gun-schema-GunSchemaValidator.html +5 -0
- package/docs/api/module-storage_gun-schema.GunSchemaValidator.html +3 -0
- package/docs/api/module-storage_gun-schema.html +3 -0
- package/docs/api/module-storage_gun-wrapper.html +11 -0
- package/docs/api/module-storage_indexeddb-storage-IndexedDBStorage.html +5 -0
- package/docs/api/module-storage_indexeddb-storage.IndexedDBStorage.html +3 -0
- package/docs/api/module-storage_indexeddb-storage.html +3 -0
- package/docs/api/module-storage_key-storage-simple.html +3 -0
- package/docs/api/module-storage_key-storage.html +4 -0
- package/docs/api/module-storage_memory-storage-MemoryStorage.html +5 -0
- package/docs/api/module-storage_memory-storage.MemoryStorage.html +3 -0
- package/docs/api/module-storage_memory-storage.html +3 -0
- package/docs/api/module-storage_migration-MigrationTool.html +6 -0
- package/docs/api/module-storage_migration.MigrationTool.html +3 -0
- package/docs/api/module-storage_migration.html +3 -0
- package/docs/api/module-storage_nostr-async.html +18 -0
- package/docs/api/module-storage_nostr-client-LRUCache.html +3 -0
- package/docs/api/module-storage_nostr-client-NostrClient.html +7 -0
- package/docs/api/module-storage_nostr-client.NostrClient.html +15 -0
- package/docs/api/module-storage_nostr-client.html +6 -0
- package/docs/api/module-storage_nostr-wrapper.html +3 -0
- package/docs/api/module-storage_outbox-queue-OutboxQueue.html +4 -0
- package/docs/api/module-storage_outbox-queue.OutboxQueue.html +3 -0
- package/docs/api/module-storage_outbox-queue.html +3 -0
- package/docs/api/module-storage_persistent-storage-PersistentStorage.html +3 -0
- package/docs/api/module-storage_persistent-storage.html +4 -0
- package/docs/api/module-storage_sync-service-SyncService.html +5 -0
- package/docs/api/module-storage_sync-service.SyncService.html +3 -0
- package/docs/api/module-storage_sync-service.html +3 -0
- package/docs/api/module-storage_unified-storage.html +3 -0
- package/docs/api/module-subscriptions_manager.SubscriptionRegistry.html +3 -0
- package/docs/api/module-subscriptions_manager.html +3 -0
- package/docs/api/schema_validator.js.html +113 -0
- package/docs/api/scripts/core.js +726 -0
- package/docs/api/scripts/core.min.js +23 -0
- package/docs/api/scripts/resize.js +90 -0
- package/docs/api/scripts/search.js +265 -0
- package/docs/api/scripts/search.min.js +6 -0
- package/docs/api/scripts/third-party/Apache-License-2.0.txt +202 -0
- package/docs/api/scripts/third-party/fuse.js +9 -0
- package/docs/api/scripts/third-party/hljs-line-num-original.js +369 -0
- package/docs/api/scripts/third-party/hljs-line-num.js +1 -0
- package/docs/api/scripts/third-party/hljs-original.js +5171 -0
- package/docs/api/scripts/third-party/hljs.js +1 -0
- package/docs/api/scripts/third-party/popper.js +5 -0
- package/docs/api/scripts/third-party/tippy.js +1 -0
- package/docs/api/scripts/third-party/tocbot.js +672 -0
- package/docs/api/scripts/third-party/tocbot.min.js +1 -0
- package/docs/api/spatial_h3-operations.js.html +129 -0
- package/docs/api/storage_backend-factory.js.html +133 -0
- package/docs/api/storage_backend-interface.js.html +164 -0
- package/docs/api/storage_backends_activitypub-backend.js.html +298 -0
- package/docs/api/storage_backends_activitypub_server.js.html +678 -0
- package/docs/api/storage_backends_gundb-backend.js.html +878 -0
- package/docs/api/storage_backends_nostr-backend.js.html +254 -0
- package/docs/api/storage_filesystem-storage-browser.js.html +83 -0
- package/docs/api/storage_filesystem-storage.js.html +207 -0
- package/docs/api/storage_global-tables.js.html +116 -0
- package/docs/api/storage_gun-async.js.html +344 -0
- package/docs/api/storage_gun-auth.js.html +376 -0
- package/docs/api/storage_gun-federation.js.html +788 -0
- package/docs/api/storage_gun-references.js.html +212 -0
- package/docs/api/storage_gun-schema.js.html +309 -0
- package/docs/api/storage_gun-wrapper.js.html +645 -0
- package/docs/api/storage_indexeddb-storage.js.html +224 -0
- package/docs/api/storage_key-storage-simple.js.html +102 -0
- package/docs/api/storage_key-storage.js.html +171 -0
- package/docs/api/storage_memory-storage.js.html +128 -0
- package/docs/api/storage_migration.js.html +354 -0
- package/docs/api/storage_nostr-async.js.html +1076 -0
- package/docs/api/storage_nostr-client.js.html +1598 -0
- package/docs/api/storage_nostr-wrapper.js.html +218 -0
- package/docs/api/storage_outbox-queue.js.html +248 -0
- package/docs/api/storage_persistent-storage.js.html +160 -0
- package/docs/api/storage_sync-service.js.html +201 -0
- package/docs/api/storage_unified-storage.js.html +157 -0
- package/docs/api/styles/clean-jsdoc-theme-base.css +1159 -0
- package/docs/api/styles/clean-jsdoc-theme-dark.css +412 -0
- package/docs/api/styles/clean-jsdoc-theme-light.css +482 -0
- package/docs/api/styles/clean-jsdoc-theme-scrollbar.css +30 -0
- package/docs/api/styles/clean-jsdoc-theme-without-scrollbar.min.css +1 -0
- package/docs/api/styles/clean-jsdoc-theme.min.css +1 -0
- package/docs/api/subscriptions_manager.js.html +162 -0
- package/docs/contracts/api-interface.md +793 -0
- package/docs/data-model.md +476 -0
- package/docs/gun-async-usage.md +338 -0
- package/docs/plan.md +349 -0
- package/docs/quickstart.md +674 -0
- package/docs/research.md +362 -0
- package/docs/spec.md +244 -0
- package/docs/storage-backends.md +326 -0
- package/docs/tasks.md +947 -0
- package/examples/demo.html +47 -0
- package/examples/holosphere-widget.js +1242 -0
- package/examples/widget-demo.html +274 -0
- package/examples/widget.html +703 -0
- package/jsdoc.json +26 -0
- package/package.json +25 -7
- package/src/ai/aggregation.js +13 -2
- package/src/ai/breakdown.js +12 -2
- package/src/ai/classifier.js +14 -3
- package/src/ai/council.js +22 -7
- package/src/ai/embeddings.js +37 -15
- package/src/ai/federation-ai.js +13 -2
- package/src/ai/h3-ai.js +14 -2
- package/src/ai/index.js +16 -7
- package/src/ai/json-ops.js +18 -5
- package/src/ai/llm-service.js +62 -31
- package/src/ai/nl-query.js +12 -2
- package/src/ai/relationships.js +13 -2
- package/src/ai/schema-extractor.js +24 -10
- package/src/ai/spatial.js +13 -2
- package/src/ai/tts.js +25 -8
- package/src/cdn-entry.js +22 -0
- package/src/content/social-protocols.js +34 -25
- package/src/contracts/abis/Appreciative.json +1280 -0
- package/src/contracts/abis/AppreciativeFactory.json +101 -0
- package/src/contracts/abis/Bundle.json +1438 -0
- package/src/contracts/abis/BundleFactory.json +106 -0
- package/src/contracts/abis/Holon.json +881 -0
- package/src/contracts/abis/Holons.json +330 -0
- package/src/contracts/abis/Managed.json +1262 -0
- package/src/contracts/abis/ManagedFactory.json +149 -0
- package/src/contracts/abis/Membrane.json +261 -0
- package/src/contracts/abis/Splitter.json +1624 -0
- package/src/contracts/abis/SplitterFactory.json +220 -0
- package/src/contracts/abis/TestToken.json +321 -0
- package/src/contracts/abis/Zoned.json +1461 -0
- package/src/contracts/abis/ZonedFactory.json +154 -0
- package/src/contracts/chain-manager.js +403 -0
- package/src/contracts/deployer.js +500 -0
- package/src/contracts/event-listener.js +539 -0
- package/src/contracts/holon-contracts.js +359 -0
- package/src/contracts/index.js +82 -0
- package/src/contracts/networks.js +229 -0
- package/src/contracts/operations.js +687 -0
- package/src/contracts/queries.js +638 -0
- package/src/core/holosphere.js +487 -6
- package/src/crypto/nostr-utils.js +303 -0
- package/src/crypto/secp256k1.js +7 -2
- package/src/federation/handshake.js +475 -0
- package/src/federation/hologram.js +117 -3
- package/src/hierarchical/upcast.js +40 -25
- package/src/index.js +1501 -1909
- package/src/lib/ai-methods.js +657 -0
- package/src/lib/contract-methods.js +442 -0
- package/src/lib/errors.js +53 -0
- package/src/lib/federation-methods.js +345 -0
- package/src/lib/index.js +30 -0
- package/src/schema/validator.js +22 -3
- package/src/spatial/h3-operations.js +19 -3
- package/src/storage/backend-factory.js +7 -2
- package/src/storage/backend-interface.js +21 -2
- package/src/storage/backends/activitypub/server.js +25 -3
- package/src/storage/backends/activitypub-backend.js +25 -2
- package/src/storage/backends/gundb-backend.js +692 -50
- package/src/storage/backends/nostr-backend.js +116 -1
- package/src/storage/filesystem-storage-browser.js +42 -2
- package/src/storage/filesystem-storage.js +72 -5
- package/src/storage/global-tables.js +35 -3
- package/src/storage/gun-async.js +75 -15
- package/src/storage/gun-auth.js +373 -0
- package/src/storage/gun-federation.js +785 -0
- package/src/storage/gun-references.js +209 -0
- package/src/storage/gun-schema.js +306 -0
- package/src/storage/gun-wrapper.js +475 -54
- package/src/storage/indexeddb-storage.js +112 -13
- package/src/storage/key-storage-simple.js +32 -9
- package/src/storage/key-storage.js +45 -13
- package/src/storage/memory-storage.js +68 -2
- package/src/storage/migration.js +20 -7
- package/src/storage/nostr-async.js +412 -122
- package/src/storage/nostr-client.js +749 -76
- package/src/storage/nostr-wrapper.js +6 -2
- package/src/storage/outbox-queue.js +55 -18
- package/src/storage/persistent-storage.js +62 -14
- package/src/storage/sync-service.js +51 -17
- package/src/storage/unified-storage.js +154 -0
- package/src/subscriptions/manager.js +34 -17
- package/types/index.d.ts +133 -0
- package/vite.config.cdn.js +60 -0
- package/tests/unit/ai/aggregation.test.js +0 -295
- package/tests/unit/ai/breakdown.test.js +0 -446
- package/tests/unit/ai/classifier.test.js +0 -294
- package/tests/unit/ai/council.test.js +0 -262
- package/tests/unit/ai/embeddings.test.js +0 -384
- package/tests/unit/ai/federation-ai.test.js +0 -344
- package/tests/unit/ai/h3-ai.test.js +0 -458
- package/tests/unit/ai/index.test.js +0 -304
- package/tests/unit/ai/json-ops.test.js +0 -307
- package/tests/unit/ai/llm-service.test.js +0 -390
- package/tests/unit/ai/nl-query.test.js +0 -383
- package/tests/unit/ai/relationships.test.js +0 -311
- package/tests/unit/ai/schema-extractor.test.js +0 -384
- package/tests/unit/ai/spatial.test.js +0 -279
- package/tests/unit/ai/tts.test.js +0 -279
- package/tests/unit/content.test.js +0 -332
- package/tests/unit/contract/core.test.js +0 -88
- package/tests/unit/contract/crypto.test.js +0 -198
- package/tests/unit/contract/data.test.js +0 -223
- package/tests/unit/contract/federation.test.js +0 -181
- package/tests/unit/contract/hierarchical.test.js +0 -113
- package/tests/unit/contract/schema.test.js +0 -114
- package/tests/unit/contract/social.test.js +0 -217
- package/tests/unit/contract/spatial.test.js +0 -110
- package/tests/unit/contract/subscriptions.test.js +0 -128
- package/tests/unit/contract/utils.test.js +0 -159
- package/tests/unit/core.test.js +0 -152
- package/tests/unit/crypto.test.js +0 -328
- package/tests/unit/federation.test.js +0 -234
- package/tests/unit/gun-async.test.js +0 -252
- package/tests/unit/hierarchical.test.js +0 -399
- package/tests/unit/integration/scenario-01-geographic-storage.test.js +0 -74
- package/tests/unit/integration/scenario-02-federation.test.js +0 -76
- package/tests/unit/integration/scenario-03-subscriptions.test.js +0 -102
- package/tests/unit/integration/scenario-04-validation.test.js +0 -129
- package/tests/unit/integration/scenario-05-hierarchy.test.js +0 -125
- package/tests/unit/integration/scenario-06-social.test.js +0 -135
- package/tests/unit/integration/scenario-07-persistence.test.js +0 -130
- package/tests/unit/integration/scenario-08-authorization.test.js +0 -161
- package/tests/unit/integration/scenario-09-cross-dimensional.test.js +0 -139
- package/tests/unit/integration/scenario-10-cross-holosphere-capabilities.test.js +0 -357
- package/tests/unit/integration/scenario-11-cross-holosphere-federation.test.js +0 -410
- package/tests/unit/integration/scenario-12-capability-federated-read.test.js +0 -719
- package/tests/unit/performance/benchmark.test.js +0 -85
- package/tests/unit/schema.test.js +0 -213
- package/tests/unit/spatial.test.js +0 -158
- package/tests/unit/storage.test.js +0 -195
- package/tests/unit/subscriptions.test.js +0 -328
- package/tests/unit/test-data-permanence-debug.js +0 -197
- package/tests/unit/test-data-permanence.js +0 -340
- package/tests/unit/test-key-persistence-fixed.js +0 -148
- package/tests/unit/test-key-persistence.js +0 -172
- package/tests/unit/test-relay-permanence.js +0 -376
- package/tests/unit/test-second-node.js +0 -95
- package/tests/unit/test-simple-write.js +0 -89
- /package/{cleanup-test-data.js → scripts/cleanup-test-data.js} +0 -0
- /package/{test-ai-real-api.js → scripts/test-ai-real-api.js} +0 -0
|
@@ -0,0 +1,785 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview GunDB Federation Module.
|
|
3
|
+
*
|
|
4
|
+
* Provides methods for creating, managing, and using federated spaces.
|
|
5
|
+
* Supports data propagation with references for lightweight federation,
|
|
6
|
+
* aggregation across federated sources, and subscription to federated changes.
|
|
7
|
+
*
|
|
8
|
+
* Ported from holosphere/federation.js to work with the GunDBBackend.
|
|
9
|
+
*
|
|
10
|
+
* @module storage/gun-federation
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Creates a federation relationship between two spaces.
|
|
15
|
+
*
|
|
16
|
+
* Federation is bidirectional by default, and data propagation uses soul references by default.
|
|
17
|
+
*
|
|
18
|
+
* @param {Object} backend - The GunDBBackend instance
|
|
19
|
+
* @param {string} spaceId1 - The first space ID
|
|
20
|
+
* @param {string} spaceId2 - The second space ID
|
|
21
|
+
* @param {boolean} [bidirectional=true] - Whether to set up bidirectional notifications
|
|
22
|
+
* @returns {Promise<boolean>} True if federation was created successfully
|
|
23
|
+
* @throws {Error} If space IDs are missing or identical
|
|
24
|
+
* @example
|
|
25
|
+
* await federate(backend, 'space1', 'space2');
|
|
26
|
+
*/
|
|
27
|
+
export async function federate(backend, spaceId1, spaceId2, bidirectional = true) {
|
|
28
|
+
if (!spaceId1 || !spaceId2) {
|
|
29
|
+
throw new Error('federate: Missing required space IDs');
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (spaceId1 === spaceId2) {
|
|
33
|
+
throw new Error('Cannot federate a space with itself');
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
try {
|
|
37
|
+
// Get or create federation info for first space (A)
|
|
38
|
+
let fedInfo1 = await backend.readGlobal('federation', spaceId1);
|
|
39
|
+
|
|
40
|
+
if (!fedInfo1) {
|
|
41
|
+
fedInfo1 = {
|
|
42
|
+
id: spaceId1,
|
|
43
|
+
name: spaceId1,
|
|
44
|
+
federation: [],
|
|
45
|
+
notify: [],
|
|
46
|
+
timestamp: Date.now()
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Ensure arrays exist
|
|
51
|
+
if (!fedInfo1.federation) fedInfo1.federation = [];
|
|
52
|
+
if (!fedInfo1.notify) fedInfo1.notify = [];
|
|
53
|
+
|
|
54
|
+
// Add space2 to space1's federation list if not already present
|
|
55
|
+
if (!fedInfo1.federation.includes(spaceId2)) {
|
|
56
|
+
fedInfo1.federation.push(spaceId2);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
fedInfo1.timestamp = Date.now();
|
|
60
|
+
await backend.writeGlobal('federation', fedInfo1);
|
|
61
|
+
|
|
62
|
+
// Handle space2 (B) - add to notify list
|
|
63
|
+
let fedInfo2 = await backend.readGlobal('federation', spaceId2);
|
|
64
|
+
|
|
65
|
+
if (!fedInfo2) {
|
|
66
|
+
fedInfo2 = {
|
|
67
|
+
id: spaceId2,
|
|
68
|
+
name: spaceId2,
|
|
69
|
+
federation: [],
|
|
70
|
+
notify: [],
|
|
71
|
+
timestamp: Date.now()
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (!fedInfo2.notify) fedInfo2.notify = [];
|
|
76
|
+
|
|
77
|
+
// Add space1 to space2's notify list
|
|
78
|
+
if (!fedInfo2.notify.includes(spaceId1)) {
|
|
79
|
+
fedInfo2.notify.push(spaceId1);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
fedInfo2.timestamp = Date.now();
|
|
83
|
+
await backend.writeGlobal('federation', fedInfo2);
|
|
84
|
+
|
|
85
|
+
// Create federation metadata record
|
|
86
|
+
const federationMeta = {
|
|
87
|
+
id: `${spaceId1}_${spaceId2}`,
|
|
88
|
+
space1: spaceId1,
|
|
89
|
+
space2: spaceId2,
|
|
90
|
+
created: Date.now(),
|
|
91
|
+
status: 'active',
|
|
92
|
+
bidirectional: bidirectional
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
await backend.writeGlobal('federationMeta', federationMeta);
|
|
96
|
+
|
|
97
|
+
return true;
|
|
98
|
+
} catch (error) {
|
|
99
|
+
console.error(`Federation creation failed: ${error.message}`);
|
|
100
|
+
throw error;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Gets federation info for a space
|
|
106
|
+
* @param {Object} backend - The GunDBBackend instance
|
|
107
|
+
* @param {string} spaceId - The space ID
|
|
108
|
+
* @returns {Promise<Object|null>} Federation info or null if not found
|
|
109
|
+
*/
|
|
110
|
+
export async function getFederation(backend, spaceId) {
|
|
111
|
+
if (!spaceId) {
|
|
112
|
+
throw new Error('getFederation: Missing space ID');
|
|
113
|
+
}
|
|
114
|
+
return backend.readGlobal('federation', spaceId);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Removes a federation relationship between spaces
|
|
119
|
+
* @param {Object} backend - The GunDBBackend instance
|
|
120
|
+
* @param {string} spaceId1 - The first space ID
|
|
121
|
+
* @param {string} spaceId2 - The second space ID
|
|
122
|
+
* @returns {Promise<boolean>} True if federation was removed successfully
|
|
123
|
+
*/
|
|
124
|
+
export async function unfederate(backend, spaceId1, spaceId2) {
|
|
125
|
+
if (!spaceId1 || !spaceId2) {
|
|
126
|
+
throw new Error('unfederate: Missing required space IDs');
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
try {
|
|
130
|
+
// Update first space federation info
|
|
131
|
+
let fedInfo1 = await backend.readGlobal('federation', spaceId1);
|
|
132
|
+
|
|
133
|
+
if (fedInfo1 && fedInfo1.federation) {
|
|
134
|
+
fedInfo1.federation = fedInfo1.federation.filter(id => id !== spaceId2);
|
|
135
|
+
fedInfo1.timestamp = Date.now();
|
|
136
|
+
await backend.writeGlobal('federation', fedInfo1);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Update second space federation info
|
|
140
|
+
let fedInfo2 = await backend.readGlobal('federation', spaceId2);
|
|
141
|
+
|
|
142
|
+
if (fedInfo2 && fedInfo2.notify) {
|
|
143
|
+
fedInfo2.notify = fedInfo2.notify.filter(id => id !== spaceId1);
|
|
144
|
+
fedInfo2.timestamp = Date.now();
|
|
145
|
+
await backend.writeGlobal('federation', fedInfo2);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Update federation metadata
|
|
149
|
+
const metaId = `${spaceId1}_${spaceId2}`;
|
|
150
|
+
const altMetaId = `${spaceId2}_${spaceId1}`;
|
|
151
|
+
|
|
152
|
+
let meta = await backend.readGlobal('federationMeta', metaId);
|
|
153
|
+
if (!meta) {
|
|
154
|
+
meta = await backend.readGlobal('federationMeta', altMetaId);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if (meta) {
|
|
158
|
+
meta.status = 'inactive';
|
|
159
|
+
meta.endedAt = Date.now();
|
|
160
|
+
await backend.writeGlobal('federationMeta', meta);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return true;
|
|
164
|
+
} catch (error) {
|
|
165
|
+
console.error(`Federation removal failed: ${error.message}`);
|
|
166
|
+
throw error;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Removes a notification relationship between two spaces
|
|
172
|
+
* @param {Object} backend - The GunDBBackend instance
|
|
173
|
+
* @param {string} spaceId1 - The space to modify
|
|
174
|
+
* @param {string} spaceId2 - The space to be removed from notifications
|
|
175
|
+
* @returns {Promise<boolean>} True if notification was removed successfully
|
|
176
|
+
*/
|
|
177
|
+
export async function removeNotify(backend, spaceId1, spaceId2) {
|
|
178
|
+
if (!spaceId1 || !spaceId2) {
|
|
179
|
+
throw new Error('removeNotify: Missing required space IDs');
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
try {
|
|
183
|
+
let fedInfo = await backend.readGlobal('federation', spaceId1);
|
|
184
|
+
|
|
185
|
+
if (!fedInfo) {
|
|
186
|
+
throw new Error(`No federation info found for ${spaceId1}`);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (!fedInfo.notify) fedInfo.notify = [];
|
|
190
|
+
|
|
191
|
+
if (fedInfo.notify.includes(spaceId2)) {
|
|
192
|
+
fedInfo.notify = fedInfo.notify.filter(id => id !== spaceId2);
|
|
193
|
+
fedInfo.timestamp = Date.now();
|
|
194
|
+
await backend.writeGlobal('federation', fedInfo);
|
|
195
|
+
return true;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
return false;
|
|
199
|
+
} catch (error) {
|
|
200
|
+
console.error(`Remove notification failed: ${error.message}`);
|
|
201
|
+
throw error;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Resets all federation relationships for a space
|
|
207
|
+
* @param {Object} backend - The GunDBBackend instance
|
|
208
|
+
* @param {string} spaceId - The ID of the space to reset
|
|
209
|
+
* @param {Object} options - Reset options
|
|
210
|
+
* @param {boolean} options.notifyPartners - Whether to notify federation partners (default: true)
|
|
211
|
+
* @param {string} options.spaceName - Optional name for the space
|
|
212
|
+
* @returns {Promise<Object>} Result object with success/error info
|
|
213
|
+
*/
|
|
214
|
+
export async function resetFederation(backend, spaceId, options = {}) {
|
|
215
|
+
if (!spaceId) {
|
|
216
|
+
throw new Error('resetFederation: Missing required space ID');
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
const {
|
|
220
|
+
notifyPartners = true,
|
|
221
|
+
spaceName = null
|
|
222
|
+
} = options;
|
|
223
|
+
|
|
224
|
+
const result = {
|
|
225
|
+
success: false,
|
|
226
|
+
federatedCount: 0,
|
|
227
|
+
notifyCount: 0,
|
|
228
|
+
partnersNotified: 0,
|
|
229
|
+
errors: []
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
try {
|
|
233
|
+
const fedInfo = await getFederation(backend, spaceId);
|
|
234
|
+
|
|
235
|
+
if (!fedInfo) {
|
|
236
|
+
return {
|
|
237
|
+
...result,
|
|
238
|
+
success: true,
|
|
239
|
+
message: 'No federation configuration found for this space'
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
result.federatedCount = fedInfo.federation?.length || 0;
|
|
244
|
+
result.notifyCount = fedInfo.notify?.length || 0;
|
|
245
|
+
|
|
246
|
+
// Create empty federation record
|
|
247
|
+
const emptyFedInfo = {
|
|
248
|
+
id: spaceId,
|
|
249
|
+
name: spaceName || spaceId,
|
|
250
|
+
federation: [],
|
|
251
|
+
notify: [],
|
|
252
|
+
timestamp: Date.now()
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
await backend.writeGlobal('federation', emptyFedInfo);
|
|
256
|
+
|
|
257
|
+
// Notify federation partners if requested
|
|
258
|
+
if (notifyPartners && fedInfo.federation && fedInfo.federation.length > 0) {
|
|
259
|
+
for (const partnerSpace of fedInfo.federation) {
|
|
260
|
+
try {
|
|
261
|
+
const partnerFedInfo = await getFederation(backend, partnerSpace);
|
|
262
|
+
|
|
263
|
+
if (partnerFedInfo) {
|
|
264
|
+
if (partnerFedInfo.federation) {
|
|
265
|
+
partnerFedInfo.federation = partnerFedInfo.federation.filter(
|
|
266
|
+
id => id !== spaceId.toString()
|
|
267
|
+
);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
if (partnerFedInfo.notify) {
|
|
271
|
+
partnerFedInfo.notify = partnerFedInfo.notify.filter(
|
|
272
|
+
id => id !== spaceId.toString()
|
|
273
|
+
);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
partnerFedInfo.timestamp = Date.now();
|
|
277
|
+
await backend.writeGlobal('federation', partnerFedInfo);
|
|
278
|
+
result.partnersNotified++;
|
|
279
|
+
}
|
|
280
|
+
} catch (error) {
|
|
281
|
+
result.errors.push({
|
|
282
|
+
partner: partnerSpace,
|
|
283
|
+
error: error.message
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// Update federation metadata records
|
|
290
|
+
if (fedInfo.federation && fedInfo.federation.length > 0) {
|
|
291
|
+
for (const partnerSpace of fedInfo.federation) {
|
|
292
|
+
try {
|
|
293
|
+
const metaId = `${spaceId}_${partnerSpace}`;
|
|
294
|
+
const altMetaId = `${partnerSpace}_${spaceId}`;
|
|
295
|
+
|
|
296
|
+
let meta = await backend.readGlobal('federationMeta', metaId);
|
|
297
|
+
if (!meta) {
|
|
298
|
+
meta = await backend.readGlobal('federationMeta', altMetaId);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
if (meta) {
|
|
302
|
+
meta.status = 'inactive';
|
|
303
|
+
meta.endedAt = Date.now();
|
|
304
|
+
await backend.writeGlobal('federationMeta', meta);
|
|
305
|
+
}
|
|
306
|
+
} catch (error) {
|
|
307
|
+
// Ignore metadata errors
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
result.success = true;
|
|
313
|
+
return result;
|
|
314
|
+
} catch (error) {
|
|
315
|
+
console.error(`Federation reset failed: ${error.message}`);
|
|
316
|
+
return {
|
|
317
|
+
...result,
|
|
318
|
+
success: false,
|
|
319
|
+
error: error.message
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Propagates data to federated spaces
|
|
326
|
+
* @param {Object} backend - The GunDBBackend instance
|
|
327
|
+
* @param {string} holon - The holon identifier
|
|
328
|
+
* @param {string} lens - The lens identifier
|
|
329
|
+
* @param {Object} data - The data to propagate
|
|
330
|
+
* @param {Object} options - Propagation options
|
|
331
|
+
* @param {boolean} options.useReferences - Whether to use references instead of duplicating data (default: true)
|
|
332
|
+
* @param {string[]} options.targetSpaces - Specific target spaces to propagate to
|
|
333
|
+
* @returns {Promise<Object>} Result with success count and errors
|
|
334
|
+
*/
|
|
335
|
+
export async function propagate(backend, holon, lens, data, options = {}) {
|
|
336
|
+
if (!backend || !holon || !lens || !data) {
|
|
337
|
+
throw new Error('propagate: Missing required parameters');
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
const {
|
|
341
|
+
useReferences = true,
|
|
342
|
+
targetSpaces = null
|
|
343
|
+
} = options;
|
|
344
|
+
|
|
345
|
+
const result = {
|
|
346
|
+
success: 0,
|
|
347
|
+
errors: 0,
|
|
348
|
+
errorDetails: [],
|
|
349
|
+
propagated: false,
|
|
350
|
+
referencesUsed: useReferences
|
|
351
|
+
};
|
|
352
|
+
|
|
353
|
+
try {
|
|
354
|
+
const fedInfo = await getFederation(backend, holon);
|
|
355
|
+
|
|
356
|
+
if (!fedInfo || !fedInfo.federation || fedInfo.federation.length === 0) {
|
|
357
|
+
return {
|
|
358
|
+
...result,
|
|
359
|
+
message: `No federation found for ${holon}`
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
if (!fedInfo.notify || fedInfo.notify.length === 0) {
|
|
364
|
+
return {
|
|
365
|
+
...result,
|
|
366
|
+
message: `No notification targets found for ${holon}`
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// Filter spaces to those in notify list
|
|
371
|
+
let spaces = fedInfo.notify;
|
|
372
|
+
|
|
373
|
+
if (targetSpaces && Array.isArray(targetSpaces) && targetSpaces.length > 0) {
|
|
374
|
+
spaces = spaces.filter(space => targetSpaces.includes(space));
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
if (spaces.length === 0) {
|
|
378
|
+
return {
|
|
379
|
+
...result,
|
|
380
|
+
message: 'No valid target spaces found after filtering'
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
const isAlreadyReference = backend.isReference(data);
|
|
385
|
+
|
|
386
|
+
// Propagate to each target space
|
|
387
|
+
for (const targetSpace of spaces) {
|
|
388
|
+
try {
|
|
389
|
+
if (useReferences && !isAlreadyReference) {
|
|
390
|
+
// Create a reference
|
|
391
|
+
const reference = backend.createReference(holon, lens, data);
|
|
392
|
+
|
|
393
|
+
reference._federation = {
|
|
394
|
+
origin: holon,
|
|
395
|
+
lens: lens,
|
|
396
|
+
timestamp: Date.now()
|
|
397
|
+
};
|
|
398
|
+
|
|
399
|
+
// Build path and write reference
|
|
400
|
+
const path = backend.buildPath(backend.appName, targetSpace, lens, reference.id);
|
|
401
|
+
await backend.write(path, reference);
|
|
402
|
+
|
|
403
|
+
result.success++;
|
|
404
|
+
} else if (isAlreadyReference) {
|
|
405
|
+
// Propagate existing reference
|
|
406
|
+
const referenceToStore = {
|
|
407
|
+
...data,
|
|
408
|
+
_federation: data._federation || {
|
|
409
|
+
origin: holon,
|
|
410
|
+
lens: lens,
|
|
411
|
+
timestamp: Date.now()
|
|
412
|
+
}
|
|
413
|
+
};
|
|
414
|
+
|
|
415
|
+
const path = backend.buildPath(backend.appName, targetSpace, lens, data.id);
|
|
416
|
+
await backend.write(path, referenceToStore);
|
|
417
|
+
result.success++;
|
|
418
|
+
} else {
|
|
419
|
+
// Store full copy
|
|
420
|
+
const dataToStore = {
|
|
421
|
+
...data,
|
|
422
|
+
_federation: {
|
|
423
|
+
origin: holon,
|
|
424
|
+
lens: lens,
|
|
425
|
+
timestamp: Date.now()
|
|
426
|
+
}
|
|
427
|
+
};
|
|
428
|
+
|
|
429
|
+
const path = backend.buildPath(backend.appName, targetSpace, lens, data.id);
|
|
430
|
+
await backend.write(path, dataToStore);
|
|
431
|
+
result.success++;
|
|
432
|
+
}
|
|
433
|
+
} catch (error) {
|
|
434
|
+
result.errors++;
|
|
435
|
+
result.errorDetails.push({
|
|
436
|
+
space: targetSpace,
|
|
437
|
+
error: error.message
|
|
438
|
+
});
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
result.propagated = result.success > 0;
|
|
443
|
+
return result;
|
|
444
|
+
} catch (error) {
|
|
445
|
+
console.error('Error in propagate:', error);
|
|
446
|
+
return {
|
|
447
|
+
...result,
|
|
448
|
+
error: error.message
|
|
449
|
+
};
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
/**
|
|
454
|
+
* Get and combine data from local and federated sources
|
|
455
|
+
* @param {Object} backend - The GunDBBackend instance
|
|
456
|
+
* @param {string} holon - The local holon name
|
|
457
|
+
* @param {string} lens - The lens to query
|
|
458
|
+
* @param {Object} options - Options for data retrieval and aggregation
|
|
459
|
+
* @returns {Promise<Array>} Combined array of local and federated data
|
|
460
|
+
*/
|
|
461
|
+
export async function getFederated(backend, holon, lens, options = {}) {
|
|
462
|
+
const {
|
|
463
|
+
aggregate = false,
|
|
464
|
+
idField = 'id',
|
|
465
|
+
sumFields = [],
|
|
466
|
+
concatArrays = [],
|
|
467
|
+
removeDuplicates = true,
|
|
468
|
+
mergeStrategy = null,
|
|
469
|
+
includeLocal = true,
|
|
470
|
+
includeFederated = true,
|
|
471
|
+
resolveReferences = true,
|
|
472
|
+
maxFederatedSpaces = -1,
|
|
473
|
+
timeout = 10000
|
|
474
|
+
} = options;
|
|
475
|
+
|
|
476
|
+
if (!backend || !holon || !lens) {
|
|
477
|
+
throw new Error('Missing required parameters: backend, holon, and lens are required');
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
const fedInfo = await getFederation(backend, holon);
|
|
481
|
+
|
|
482
|
+
const result = [];
|
|
483
|
+
const processedIds = new Set();
|
|
484
|
+
|
|
485
|
+
// Process federated spaces first
|
|
486
|
+
if (includeFederated && fedInfo && fedInfo.federation && fedInfo.federation.length > 0) {
|
|
487
|
+
const federatedSpaces = maxFederatedSpaces === -1
|
|
488
|
+
? fedInfo.federation
|
|
489
|
+
: fedInfo.federation.slice(0, maxFederatedSpaces);
|
|
490
|
+
|
|
491
|
+
for (const federatedSpace of federatedSpaces) {
|
|
492
|
+
try {
|
|
493
|
+
const path = backend.buildPath(backend.appName, federatedSpace, lens);
|
|
494
|
+
const federatedItems = await backend.readAll(path);
|
|
495
|
+
|
|
496
|
+
for (const item of federatedItems) {
|
|
497
|
+
if (!item || !item[idField]) continue;
|
|
498
|
+
|
|
499
|
+
result.push(item);
|
|
500
|
+
processedIds.add(item[idField]);
|
|
501
|
+
}
|
|
502
|
+
} catch (error) {
|
|
503
|
+
console.warn(`Error processing federated space ${federatedSpace}: ${error.message}`);
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
// Get local data
|
|
509
|
+
if (includeLocal) {
|
|
510
|
+
const localPath = backend.buildPath(backend.appName, holon, lens);
|
|
511
|
+
const localData = await backend.readAll(localPath);
|
|
512
|
+
|
|
513
|
+
for (const item of localData) {
|
|
514
|
+
if (item && item[idField] && !processedIds.has(item[idField])) {
|
|
515
|
+
result.push(item);
|
|
516
|
+
processedIds.add(item[idField]);
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
// Resolve references
|
|
522
|
+
if (resolveReferences) {
|
|
523
|
+
for (let i = 0; i < result.length; i++) {
|
|
524
|
+
const item = result[i];
|
|
525
|
+
|
|
526
|
+
if (item.soul && item.id) {
|
|
527
|
+
// Simple reference format
|
|
528
|
+
try {
|
|
529
|
+
const resolved = await backend.resolveReference(item);
|
|
530
|
+
if (resolved) {
|
|
531
|
+
result[i] = resolved;
|
|
532
|
+
}
|
|
533
|
+
} catch (error) {
|
|
534
|
+
result[i] = {
|
|
535
|
+
id: item.id,
|
|
536
|
+
_federation: {
|
|
537
|
+
isReference: true,
|
|
538
|
+
resolved: false,
|
|
539
|
+
soul: item.soul,
|
|
540
|
+
error: error.message,
|
|
541
|
+
timestamp: Date.now()
|
|
542
|
+
}
|
|
543
|
+
};
|
|
544
|
+
}
|
|
545
|
+
} else if (item._federation && item._federation.isReference) {
|
|
546
|
+
// Legacy reference format
|
|
547
|
+
try {
|
|
548
|
+
const resolved = await backend.resolveReference(item);
|
|
549
|
+
if (resolved) {
|
|
550
|
+
result[i] = resolved;
|
|
551
|
+
}
|
|
552
|
+
} catch (error) {
|
|
553
|
+
console.warn(`Error resolving legacy reference: ${error.message}`);
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
// Apply aggregation if requested
|
|
560
|
+
if (aggregate && result.length > 0) {
|
|
561
|
+
const groupedById = result.reduce((acc, item) => {
|
|
562
|
+
const id = item[idField];
|
|
563
|
+
if (!acc[id]) {
|
|
564
|
+
acc[id] = [];
|
|
565
|
+
}
|
|
566
|
+
acc[id].push(item);
|
|
567
|
+
return acc;
|
|
568
|
+
}, {});
|
|
569
|
+
|
|
570
|
+
const aggregatedData = Object.values(groupedById).map(group => {
|
|
571
|
+
if (group.length === 1) return group[0];
|
|
572
|
+
|
|
573
|
+
if (mergeStrategy && typeof mergeStrategy === 'function') {
|
|
574
|
+
return mergeStrategy(group);
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
const base = { ...group[0] };
|
|
578
|
+
|
|
579
|
+
for (const field of sumFields) {
|
|
580
|
+
if (typeof base[field] === 'number') {
|
|
581
|
+
base[field] = group.reduce((sum, item) => sum + (Number(item[field]) || 0), 0);
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
for (const field of concatArrays) {
|
|
586
|
+
if (Array.isArray(base[field])) {
|
|
587
|
+
const allValues = group.reduce((all, item) => {
|
|
588
|
+
return Array.isArray(item[field]) ? [...all, ...item[field]] : all;
|
|
589
|
+
}, []);
|
|
590
|
+
|
|
591
|
+
base[field] = removeDuplicates ? Array.from(new Set(allValues)) : allValues;
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
base._aggregated = {
|
|
596
|
+
count: group.length,
|
|
597
|
+
timestamp: Date.now()
|
|
598
|
+
};
|
|
599
|
+
|
|
600
|
+
return base;
|
|
601
|
+
});
|
|
602
|
+
|
|
603
|
+
return aggregatedData;
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
return result;
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
/**
|
|
610
|
+
* Subscribes to federation notifications for a space
|
|
611
|
+
* @param {Object} backend - The GunDBBackend instance
|
|
612
|
+
* @param {string} spaceId - The space ID to subscribe to
|
|
613
|
+
* @param {Function} callback - The callback to execute on notifications
|
|
614
|
+
* @param {Object} options - Subscription options
|
|
615
|
+
* @returns {Promise<Object>} Subscription object with unsubscribe() method
|
|
616
|
+
*/
|
|
617
|
+
export async function subscribeFederation(backend, spaceId, callback, options = {}) {
|
|
618
|
+
if (!spaceId || !callback) {
|
|
619
|
+
throw new Error('subscribeFederation: Missing required parameters');
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
const { lenses = ['*'], throttle = 0, resolveReferences = true } = options;
|
|
623
|
+
|
|
624
|
+
const fedInfo = await getFederation(backend, spaceId);
|
|
625
|
+
if (!fedInfo) {
|
|
626
|
+
throw new Error('No federation info found for space');
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
const subscriptions = [];
|
|
630
|
+
let lastNotificationTime = {};
|
|
631
|
+
|
|
632
|
+
if (fedInfo.federation && fedInfo.federation.length > 0) {
|
|
633
|
+
for (const federatedSpace of fedInfo.federation) {
|
|
634
|
+
for (const lens of lenses) {
|
|
635
|
+
try {
|
|
636
|
+
const path = backend.buildPath(backend.appName, federatedSpace, lens);
|
|
637
|
+
|
|
638
|
+
const sub = await backend.subscribe(path, async (data, key) => {
|
|
639
|
+
try {
|
|
640
|
+
if (!data || !data.id) return;
|
|
641
|
+
|
|
642
|
+
// Apply throttling
|
|
643
|
+
const now = Date.now();
|
|
644
|
+
const throttleKey = `${federatedSpace}_${lens}_${data.id}`;
|
|
645
|
+
|
|
646
|
+
if (throttle > 0) {
|
|
647
|
+
if (lastNotificationTime[throttleKey] &&
|
|
648
|
+
(now - lastNotificationTime[throttleKey]) < throttle) {
|
|
649
|
+
return;
|
|
650
|
+
}
|
|
651
|
+
lastNotificationTime[throttleKey] = now;
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
// Resolve reference if needed
|
|
655
|
+
let resolvedData = data;
|
|
656
|
+
if (resolveReferences && backend.isReference(data)) {
|
|
657
|
+
resolvedData = await backend.resolveReference(data);
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
if (!resolvedData._federation) {
|
|
661
|
+
resolvedData._federation = {
|
|
662
|
+
origin: federatedSpace,
|
|
663
|
+
timestamp: now
|
|
664
|
+
};
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
await callback(resolvedData, federatedSpace, lens);
|
|
668
|
+
} catch (error) {
|
|
669
|
+
console.warn('Federation notification error:', error);
|
|
670
|
+
}
|
|
671
|
+
}, { resolveReferences: false }); // Don't resolve in subscribe, we handle it
|
|
672
|
+
|
|
673
|
+
if (sub && typeof sub.unsubscribe === 'function') {
|
|
674
|
+
subscriptions.push(sub);
|
|
675
|
+
}
|
|
676
|
+
} catch (error) {
|
|
677
|
+
console.warn(`Error creating subscription for ${federatedSpace}/${lens}:`, error);
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
return {
|
|
684
|
+
unsubscribe: () => {
|
|
685
|
+
subscriptions.forEach(sub => {
|
|
686
|
+
try {
|
|
687
|
+
if (sub && typeof sub.unsubscribe === 'function') {
|
|
688
|
+
sub.unsubscribe();
|
|
689
|
+
}
|
|
690
|
+
} catch (error) {
|
|
691
|
+
console.warn('Error unsubscribing:', error);
|
|
692
|
+
}
|
|
693
|
+
});
|
|
694
|
+
subscriptions.length = 0;
|
|
695
|
+
lastNotificationTime = {};
|
|
696
|
+
},
|
|
697
|
+
getSubscriptionCount: () => subscriptions.length
|
|
698
|
+
};
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
/**
|
|
702
|
+
* Tracks a federated message across different chats
|
|
703
|
+
* @param {Object} backend - The GunDBBackend instance
|
|
704
|
+
* @param {string} originalChatId - The ID of the original chat
|
|
705
|
+
* @param {string} messageId - The ID of the original message
|
|
706
|
+
* @param {string} federatedChatId - The ID of the federated chat
|
|
707
|
+
* @param {string} federatedMessageId - The ID of the message in the federated chat
|
|
708
|
+
* @param {string} type - The type of message
|
|
709
|
+
* @returns {Promise<void>}
|
|
710
|
+
*/
|
|
711
|
+
export async function federateMessage(backend, originalChatId, messageId, federatedChatId, federatedMessageId, type = 'generic') {
|
|
712
|
+
const trackingKey = `${originalChatId}_${messageId}_fedmsgs`;
|
|
713
|
+
let tracking = await backend.readGlobal('federation_messages', trackingKey);
|
|
714
|
+
|
|
715
|
+
if (!tracking) {
|
|
716
|
+
tracking = {
|
|
717
|
+
id: trackingKey,
|
|
718
|
+
originalChatId,
|
|
719
|
+
originalMessageId: messageId,
|
|
720
|
+
type,
|
|
721
|
+
messages: []
|
|
722
|
+
};
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
const existingMsg = tracking.messages.find(m => m.chatId === federatedChatId);
|
|
726
|
+
if (existingMsg) {
|
|
727
|
+
existingMsg.messageId = federatedMessageId;
|
|
728
|
+
existingMsg.timestamp = Date.now();
|
|
729
|
+
} else {
|
|
730
|
+
tracking.messages.push({
|
|
731
|
+
chatId: federatedChatId,
|
|
732
|
+
messageId: federatedMessageId,
|
|
733
|
+
timestamp: Date.now()
|
|
734
|
+
});
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
await backend.writeGlobal('federation_messages', tracking);
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
/**
|
|
741
|
+
* Gets all federated messages for a given original message
|
|
742
|
+
* @param {Object} backend - The GunDBBackend instance
|
|
743
|
+
* @param {string} originalChatId - The ID of the original chat
|
|
744
|
+
* @param {string} messageId - The ID of the original message
|
|
745
|
+
* @returns {Promise<Object|null>} The tracking information for the message
|
|
746
|
+
*/
|
|
747
|
+
export async function getFederatedMessages(backend, originalChatId, messageId) {
|
|
748
|
+
const trackingKey = `${originalChatId}_${messageId}_fedmsgs`;
|
|
749
|
+
return backend.readGlobal('federation_messages', trackingKey);
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
/**
|
|
753
|
+
* Updates a federated message across all federated chats
|
|
754
|
+
* @param {Object} backend - The GunDBBackend instance
|
|
755
|
+
* @param {string} originalChatId - The ID of the original chat
|
|
756
|
+
* @param {string} messageId - The ID of the original message
|
|
757
|
+
* @param {Function} updateCallback - Function to update the message in each chat
|
|
758
|
+
* @returns {Promise<void>}
|
|
759
|
+
*/
|
|
760
|
+
export async function updateFederatedMessages(backend, originalChatId, messageId, updateCallback) {
|
|
761
|
+
const tracking = await getFederatedMessages(backend, originalChatId, messageId);
|
|
762
|
+
if (!tracking?.messages) return;
|
|
763
|
+
|
|
764
|
+
for (const msg of tracking.messages) {
|
|
765
|
+
try {
|
|
766
|
+
await updateCallback(msg.chatId, msg.messageId);
|
|
767
|
+
} catch (error) {
|
|
768
|
+
console.warn(`Failed to update federated message in chat ${msg.chatId}:`, error);
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
export default {
|
|
774
|
+
federate,
|
|
775
|
+
unfederate,
|
|
776
|
+
getFederation,
|
|
777
|
+
resetFederation,
|
|
778
|
+
removeNotify,
|
|
779
|
+
propagate,
|
|
780
|
+
getFederated,
|
|
781
|
+
subscribeFederation,
|
|
782
|
+
federateMessage,
|
|
783
|
+
getFederatedMessages,
|
|
784
|
+
updateFederatedMessages
|
|
785
|
+
};
|