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
|
@@ -1,39 +1,112 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* GunDB
|
|
3
|
-
*
|
|
2
|
+
* @fileoverview GunDB storage backend with authentication, schema validation, references, and global tables.
|
|
3
|
+
* Provides full-featured P2P data storage using GunDB with write caching for immediate consistency.
|
|
4
|
+
* @module storage/backends/gundb-backend
|
|
4
5
|
*/
|
|
5
6
|
|
|
6
7
|
import { StorageBackend } from '../backend-interface.js';
|
|
7
8
|
import * as wrapper from '../gun-wrapper.js';
|
|
8
9
|
import { gunPromise, gunPut, gunMap, gunCollect } from '../gun-async.js';
|
|
10
|
+
import { GunReferenceHandler } from '../gun-references.js';
|
|
11
|
+
import { GunAuth } from '../gun-auth.js';
|
|
12
|
+
import { GunSchemaValidator } from '../gun-schema.js';
|
|
9
13
|
|
|
14
|
+
/**
|
|
15
|
+
* GunDB storage backend implementation with advanced features.
|
|
16
|
+
* Supports authentication, schema validation, references, global tables,
|
|
17
|
+
* and write caching for immediate consistency.
|
|
18
|
+
*
|
|
19
|
+
* @class GunDBBackend
|
|
20
|
+
* @extends StorageBackend
|
|
21
|
+
* @example
|
|
22
|
+
* const backend = await BackendFactory.create('gundb', {
|
|
23
|
+
* appName: 'myapp',
|
|
24
|
+
* peers: ['wss://gun-server.example.com/gun'],
|
|
25
|
+
* radisk: true
|
|
26
|
+
* });
|
|
27
|
+
*/
|
|
10
28
|
export class GunDBBackend extends StorageBackend {
|
|
29
|
+
/**
|
|
30
|
+
* Create a new GunDB backend instance.
|
|
31
|
+
* @param {Object} config - Backend configuration
|
|
32
|
+
* @param {string} [config.appName='holosphere'] - Application namespace
|
|
33
|
+
* @param {string[]} [config.peers=[]] - Array of peer URLs
|
|
34
|
+
* @param {boolean} [config.radisk=true] - Enable radisk persistence
|
|
35
|
+
* @param {boolean} [config.localStorage=true] - Enable localStorage
|
|
36
|
+
* @param {string} [config.dataDir='radata'] - Data directory for Node.js
|
|
37
|
+
* @param {string} [config.privateKey] - Optional existing private key
|
|
38
|
+
* @param {string} [config.publicKey] - Optional existing public key
|
|
39
|
+
* @param {boolean} [config.strict=false] - Enable strict schema validation
|
|
40
|
+
*/
|
|
11
41
|
constructor(config) {
|
|
12
42
|
super(config);
|
|
13
43
|
this.gun = null;
|
|
14
44
|
this.keyPair = null;
|
|
45
|
+
this.appName = config.appName || 'holosphere';
|
|
46
|
+
|
|
47
|
+
// New modules
|
|
48
|
+
this.references = null;
|
|
49
|
+
this.auth = null;
|
|
50
|
+
this.schemaValidator = null;
|
|
51
|
+
|
|
52
|
+
// Subscription tracking
|
|
53
|
+
this.subscriptions = new Map();
|
|
54
|
+
this.subscriptionCounter = 0;
|
|
55
|
+
|
|
56
|
+
// Write cache for immediate consistency
|
|
57
|
+
// Gun's readAll/map().once() doesn't immediately see new writes - this cache bridges that gap
|
|
58
|
+
this.writeCache = new Map(); // path -> Map(id -> {data, timestamp})
|
|
59
|
+
this.writeCacheTTL = 300000; // Cache entries expire after 5 minutes (increased from 30s)
|
|
60
|
+
|
|
61
|
+
// Pending writes queue for retry
|
|
62
|
+
this.pendingWrites = new Map(); // path -> {data, retries, lastAttempt}
|
|
63
|
+
this.maxWriteRetries = 5;
|
|
64
|
+
this.writeRetryInterval = 10000; // 10 seconds between retries
|
|
15
65
|
}
|
|
16
66
|
|
|
17
67
|
async init() {
|
|
18
68
|
// Dynamically import Gun to avoid issues if not installed
|
|
19
69
|
let Gun;
|
|
20
70
|
try {
|
|
71
|
+
console.log('[gundb-backend] Importing Gun...');
|
|
21
72
|
const gunModule = await import('gun');
|
|
22
73
|
Gun = gunModule.default || gunModule;
|
|
74
|
+
console.log('[gundb-backend] Gun imported:', typeof Gun);
|
|
23
75
|
} catch (error) {
|
|
24
76
|
throw new Error(
|
|
25
77
|
'GunDB backend requires the "gun" package. Install it with: npm install gun'
|
|
26
78
|
);
|
|
27
79
|
}
|
|
28
80
|
|
|
81
|
+
// In Node.js, Gun needs a file path for radisk persistence
|
|
82
|
+
// Default to 'radata' if not specified
|
|
83
|
+
const dataDir = this.config.dataDir || 'radata';
|
|
84
|
+
|
|
29
85
|
const gunConfig = {
|
|
30
86
|
peers: this.config.peers || [],
|
|
31
87
|
radisk: this.config.radisk !== false,
|
|
32
88
|
localStorage: this.config.localStorage !== false,
|
|
33
|
-
file:
|
|
89
|
+
file: dataDir,
|
|
34
90
|
};
|
|
35
91
|
|
|
92
|
+
console.log('[gundb-backend] Gun config:', JSON.stringify(gunConfig));
|
|
93
|
+
|
|
36
94
|
this.gun = Gun(gunConfig);
|
|
95
|
+
console.log('[gundb-backend] Gun instance created:', !!this.gun);
|
|
96
|
+
|
|
97
|
+
// Test basic Gun functionality
|
|
98
|
+
try {
|
|
99
|
+
const testKey = `_test_${Date.now()}`;
|
|
100
|
+
this.gun.get(testKey).put({ test: true }, (ack) => {
|
|
101
|
+
console.log('[gundb-backend] Gun test write ack:', ack);
|
|
102
|
+
});
|
|
103
|
+
console.log('[gundb-backend] Gun test write initiated');
|
|
104
|
+
} catch (e) {
|
|
105
|
+
console.error('[gundb-backend] Gun test write failed:', e.message);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Add a small delay to let Gun initialize
|
|
109
|
+
await new Promise(resolve => setTimeout(resolve, 500));
|
|
37
110
|
|
|
38
111
|
// Generate or use provided key pair using Gun's SEA
|
|
39
112
|
try {
|
|
@@ -50,28 +123,253 @@ export class GunDBBackend extends StorageBackend {
|
|
|
50
123
|
}
|
|
51
124
|
} else {
|
|
52
125
|
// SEA not available, use simple identifier
|
|
53
|
-
this.publicKey = this.
|
|
126
|
+
this.publicKey = this.appName;
|
|
54
127
|
}
|
|
55
128
|
} catch (error) {
|
|
56
129
|
// SEA might not be loaded
|
|
57
|
-
this.publicKey = this.
|
|
130
|
+
this.publicKey = this.appName;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Initialize modules
|
|
134
|
+
this.references = new GunReferenceHandler(this.appName);
|
|
135
|
+
this.auth = new GunAuth(this.gun, this.appName);
|
|
136
|
+
this.schemaValidator = new GunSchemaValidator({
|
|
137
|
+
strict: this.config.strict || false,
|
|
138
|
+
cacheMaxAge: this.config.schemaCacheMaxAge || 3600000
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
// Try to initialize schema validator (may fail if AJV not installed)
|
|
142
|
+
await this.schemaValidator.init();
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// ============================================================================
|
|
146
|
+
// WRITE CACHE (for immediate consistency)
|
|
147
|
+
// ============================================================================
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Add item to the local write cache for immediate consistency
|
|
151
|
+
* @private
|
|
152
|
+
*/
|
|
153
|
+
_addToWriteCache(path, data) {
|
|
154
|
+
if (!data || !data.id) return;
|
|
155
|
+
|
|
156
|
+
// Extract the parent path (without the data id)
|
|
157
|
+
const parentPath = path.substring(0, path.lastIndexOf('/'));
|
|
158
|
+
if (!parentPath) return;
|
|
159
|
+
|
|
160
|
+
if (!this.writeCache.has(parentPath)) {
|
|
161
|
+
this.writeCache.set(parentPath, new Map());
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const pathCache = this.writeCache.get(parentPath);
|
|
165
|
+
pathCache.set(data.id.toString(), {
|
|
166
|
+
data: data,
|
|
167
|
+
timestamp: Date.now()
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Remove item from write cache (e.g., on delete)
|
|
173
|
+
* @private
|
|
174
|
+
*/
|
|
175
|
+
_removeFromWriteCache(path, id) {
|
|
176
|
+
// Extract the parent path
|
|
177
|
+
const parentPath = path.substring(0, path.lastIndexOf('/'));
|
|
178
|
+
const pathCache = this.writeCache.get(parentPath);
|
|
179
|
+
if (pathCache) {
|
|
180
|
+
pathCache.delete(id.toString());
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Get cached writes for a path, filtering out expired entries
|
|
186
|
+
* @private
|
|
187
|
+
*/
|
|
188
|
+
_getWriteCacheEntries(path) {
|
|
189
|
+
const pathCache = this.writeCache.get(path);
|
|
190
|
+
if (!pathCache) return [];
|
|
191
|
+
|
|
192
|
+
const now = Date.now();
|
|
193
|
+
const validEntries = [];
|
|
194
|
+
|
|
195
|
+
for (const [id, entry] of pathCache) {
|
|
196
|
+
if (now - entry.timestamp < this.writeCacheTTL) {
|
|
197
|
+
validEntries.push(entry.data);
|
|
198
|
+
} else {
|
|
199
|
+
// Clean up expired entries
|
|
200
|
+
pathCache.delete(id);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
return validEntries;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Merge cached writes with results from Gun
|
|
209
|
+
* @private
|
|
210
|
+
*/
|
|
211
|
+
_mergeWithWriteCache(path, gunResults) {
|
|
212
|
+
const cachedWrites = this._getWriteCacheEntries(path);
|
|
213
|
+
if (cachedWrites.length === 0) return gunResults;
|
|
214
|
+
|
|
215
|
+
// Create a map by ID for deduplication
|
|
216
|
+
const resultMap = new Map();
|
|
217
|
+
|
|
218
|
+
// Add Gun results first
|
|
219
|
+
for (const item of gunResults) {
|
|
220
|
+
if (item && item.id) {
|
|
221
|
+
resultMap.set(item.id.toString(), item);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// Overlay cached writes (they're more recent)
|
|
226
|
+
for (const item of cachedWrites) {
|
|
227
|
+
if (item && item.id && !item._deleted) {
|
|
228
|
+
resultMap.set(item.id.toString(), item);
|
|
229
|
+
} else if (item && item.id && item._deleted) {
|
|
230
|
+
// Remove deleted items
|
|
231
|
+
resultMap.delete(item.id.toString());
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
return Array.from(resultMap.values());
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Clear write cache for a specific path or all paths
|
|
240
|
+
* @param {string} [path] - Optional path to clear, clears all if not provided
|
|
241
|
+
*/
|
|
242
|
+
clearWriteCache(path = null) {
|
|
243
|
+
if (path) {
|
|
244
|
+
this.writeCache.delete(path);
|
|
245
|
+
} else {
|
|
246
|
+
this.writeCache.clear();
|
|
58
247
|
}
|
|
59
248
|
}
|
|
60
249
|
|
|
250
|
+
// ============================================================================
|
|
251
|
+
// PATH BUILDING
|
|
252
|
+
// ============================================================================
|
|
253
|
+
|
|
61
254
|
buildPath(appName, holonId, lensName, key = null) {
|
|
62
255
|
return wrapper.buildPath(appName, holonId, lensName, key);
|
|
63
256
|
}
|
|
64
257
|
|
|
258
|
+
buildGlobalPath(tableName, key = null) {
|
|
259
|
+
return wrapper.buildGlobalPath(this.appName, tableName, key);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// ============================================================================
|
|
263
|
+
// BASIC CRUD OPERATIONS
|
|
264
|
+
// ============================================================================
|
|
265
|
+
|
|
65
266
|
async write(path, data, options = {}) {
|
|
66
|
-
|
|
267
|
+
// Validate against schema if strict mode
|
|
268
|
+
if (this.schemaValidator && this.schemaValidator.isStrict()) {
|
|
269
|
+
const pathParts = path.split('/');
|
|
270
|
+
if (pathParts.length >= 3) {
|
|
271
|
+
const lens = pathParts[2];
|
|
272
|
+
// Skip validation for references
|
|
273
|
+
if (!this.isReference(data)) {
|
|
274
|
+
const result = await this.schemaValidator.validateData(
|
|
275
|
+
this.gun, this.appName, lens, data
|
|
276
|
+
);
|
|
277
|
+
if (!result.valid) {
|
|
278
|
+
throw new Error(`Schema validation failed: ${JSON.stringify(result.errors)}`);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// Add to write cache FIRST for immediate consistency
|
|
285
|
+
// This ensures readAll returns the new data even if Gun write is slow/times out
|
|
286
|
+
this._addToWriteCache(path, data);
|
|
287
|
+
|
|
288
|
+
// Try to write to Gun with retry on timeout
|
|
289
|
+
const result = await this._writeWithRetry(path, data);
|
|
290
|
+
|
|
291
|
+
return result;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* Write to Gun with automatic retry on timeout
|
|
296
|
+
* @private
|
|
297
|
+
*/
|
|
298
|
+
async _writeWithRetry(path, data, attempt = 0) {
|
|
299
|
+
const result = await wrapper.write(this.gun, path, data);
|
|
300
|
+
|
|
301
|
+
if (result.timeout && attempt < this.maxWriteRetries) {
|
|
302
|
+
// Queue for background retry
|
|
303
|
+
this.pendingWrites.set(path, {
|
|
304
|
+
data,
|
|
305
|
+
retries: attempt + 1,
|
|
306
|
+
lastAttempt: Date.now()
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
// Schedule retry
|
|
310
|
+
this._scheduleRetry(path);
|
|
311
|
+
} else if (!result.timeout) {
|
|
312
|
+
// Success - remove from pending
|
|
313
|
+
this.pendingWrites.delete(path);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
return result;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Schedule a background retry for a pending write
|
|
321
|
+
* @private
|
|
322
|
+
*/
|
|
323
|
+
_scheduleRetry(path) {
|
|
324
|
+
setTimeout(async () => {
|
|
325
|
+
const pending = this.pendingWrites.get(path);
|
|
326
|
+
if (!pending) return;
|
|
327
|
+
|
|
328
|
+
if (pending.retries >= this.maxWriteRetries) {
|
|
329
|
+
console.warn(`[gundb-backend] Max retries reached for: ${path}`);
|
|
330
|
+
this.pendingWrites.delete(path);
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
console.log(`[gundb-backend] Retrying write (attempt ${pending.retries + 1}): ${path}`);
|
|
335
|
+
await this._writeWithRetry(path, pending.data, pending.retries);
|
|
336
|
+
}, this.writeRetryInterval);
|
|
67
337
|
}
|
|
68
338
|
|
|
69
339
|
async read(path, options = {}) {
|
|
70
|
-
|
|
340
|
+
const data = await wrapper.read(this.gun, path);
|
|
341
|
+
|
|
342
|
+
// Resolve references if requested
|
|
343
|
+
if (data && options.resolveReferences && this.isReference(data)) {
|
|
344
|
+
return this.resolveReference(data);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
return data;
|
|
71
348
|
}
|
|
72
349
|
|
|
73
350
|
async readAll(path, options = {}) {
|
|
74
|
-
|
|
351
|
+
const gunItems = await wrapper.readAll(this.gun, path);
|
|
352
|
+
|
|
353
|
+
// Merge with write cache for immediate consistency
|
|
354
|
+
const items = this._mergeWithWriteCache(path, gunItems);
|
|
355
|
+
|
|
356
|
+
// Resolve references if requested
|
|
357
|
+
if (options.resolveReferences) {
|
|
358
|
+
const resolved = [];
|
|
359
|
+
for (const item of items) {
|
|
360
|
+
if (this.isReference(item)) {
|
|
361
|
+
const resolvedItem = await this.resolveReference(item);
|
|
362
|
+
if (resolvedItem) {
|
|
363
|
+
resolved.push(resolvedItem);
|
|
364
|
+
}
|
|
365
|
+
} else {
|
|
366
|
+
resolved.push(item);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
return resolved;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
return items;
|
|
75
373
|
}
|
|
76
374
|
|
|
77
375
|
async update(path, updates) {
|
|
@@ -79,11 +377,27 @@ export class GunDBBackend extends StorageBackend {
|
|
|
79
377
|
}
|
|
80
378
|
|
|
81
379
|
async delete(path) {
|
|
82
|
-
|
|
380
|
+
const result = await wrapper.deleteData(this.gun, path);
|
|
381
|
+
|
|
382
|
+
// Extract id from path and mark as deleted in cache
|
|
383
|
+
const pathParts = path.split('/');
|
|
384
|
+
const id = pathParts[pathParts.length - 1];
|
|
385
|
+
if (id) {
|
|
386
|
+
// Add tombstone to cache so readAll doesn't return this item
|
|
387
|
+
const parentPath = path.substring(0, path.lastIndexOf('/'));
|
|
388
|
+
if (!this.writeCache.has(parentPath)) {
|
|
389
|
+
this.writeCache.set(parentPath, new Map());
|
|
390
|
+
}
|
|
391
|
+
this.writeCache.get(parentPath).set(id.toString(), {
|
|
392
|
+
data: { id, _deleted: true },
|
|
393
|
+
timestamp: Date.now()
|
|
394
|
+
});
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
return result;
|
|
83
398
|
}
|
|
84
399
|
|
|
85
400
|
async deleteAll(path) {
|
|
86
|
-
// Gun doesn't have a native deleteAll, so we iterate and delete
|
|
87
401
|
const items = await this.readAll(path);
|
|
88
402
|
let count = 0;
|
|
89
403
|
|
|
@@ -98,58 +412,343 @@ export class GunDBBackend extends StorageBackend {
|
|
|
98
412
|
return { success: true, count };
|
|
99
413
|
}
|
|
100
414
|
|
|
415
|
+
// ============================================================================
|
|
416
|
+
// GLOBAL TABLE OPERATIONS
|
|
417
|
+
// ============================================================================
|
|
418
|
+
|
|
419
|
+
async writeGlobal(tableName, data) {
|
|
420
|
+
// Add to write cache FIRST for immediate consistency
|
|
421
|
+
// This ensures readAllGlobal returns the new data even if Gun write is slow/times out
|
|
422
|
+
const path = this.buildGlobalPath(tableName, data.id);
|
|
423
|
+
this._addToWriteCache(path, data);
|
|
424
|
+
|
|
425
|
+
// Try to write to Gun with retry on timeout
|
|
426
|
+
const result = await this._writeGlobalWithRetry(tableName, data);
|
|
427
|
+
|
|
428
|
+
return result;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
/**
|
|
432
|
+
* Write to global table with automatic retry on timeout
|
|
433
|
+
* @private
|
|
434
|
+
*/
|
|
435
|
+
async _writeGlobalWithRetry(tableName, data, attempt = 0) {
|
|
436
|
+
const path = this.buildGlobalPath(tableName, data.id);
|
|
437
|
+
const result = await wrapper.writeGlobal(this.gun, this.appName, tableName, data);
|
|
438
|
+
|
|
439
|
+
if (result.timeout && attempt < this.maxWriteRetries) {
|
|
440
|
+
// Queue for background retry
|
|
441
|
+
this.pendingWrites.set(path, {
|
|
442
|
+
data: { tableName, data },
|
|
443
|
+
isGlobal: true,
|
|
444
|
+
retries: attempt + 1,
|
|
445
|
+
lastAttempt: Date.now()
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
// Schedule retry
|
|
449
|
+
this._scheduleGlobalRetry(path, tableName);
|
|
450
|
+
} else if (!result.timeout) {
|
|
451
|
+
// Success - remove from pending
|
|
452
|
+
this.pendingWrites.delete(path);
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
return result;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
/**
|
|
459
|
+
* Schedule a background retry for a pending global write
|
|
460
|
+
* @private
|
|
461
|
+
*/
|
|
462
|
+
_scheduleGlobalRetry(path, tableName) {
|
|
463
|
+
setTimeout(async () => {
|
|
464
|
+
const pending = this.pendingWrites.get(path);
|
|
465
|
+
if (!pending || !pending.isGlobal) return;
|
|
466
|
+
|
|
467
|
+
if (pending.retries >= this.maxWriteRetries) {
|
|
468
|
+
console.warn(`[gundb-backend] Max retries reached for global: ${path}`);
|
|
469
|
+
this.pendingWrites.delete(path);
|
|
470
|
+
return;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
console.log(`[gundb-backend] Retrying global write (attempt ${pending.retries + 1}): ${path}`);
|
|
474
|
+
await this._writeGlobalWithRetry(tableName, pending.data.data, pending.retries);
|
|
475
|
+
}, this.writeRetryInterval);
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
async readGlobal(tableName, key) {
|
|
479
|
+
return wrapper.readGlobal(this.gun, this.appName, tableName, key);
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
async readAllGlobal(tableName, timeout = 5000) {
|
|
483
|
+
const gunItems = await wrapper.readAllGlobal(this.gun, this.appName, tableName, timeout);
|
|
484
|
+
|
|
485
|
+
// Merge with write cache for immediate consistency
|
|
486
|
+
const path = this.buildGlobalPath(tableName);
|
|
487
|
+
return this._mergeWithWriteCache(path, gunItems);
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
async deleteGlobal(tableName, key) {
|
|
491
|
+
const result = await wrapper.deleteGlobal(this.gun, this.appName, tableName, key);
|
|
492
|
+
|
|
493
|
+
// Mark as deleted in cache
|
|
494
|
+
const path = this.buildGlobalPath(tableName);
|
|
495
|
+
if (!this.writeCache.has(path)) {
|
|
496
|
+
this.writeCache.set(path, new Map());
|
|
497
|
+
}
|
|
498
|
+
this.writeCache.get(path).set(key.toString(), {
|
|
499
|
+
data: { id: key, _deleted: true },
|
|
500
|
+
timestamp: Date.now()
|
|
501
|
+
});
|
|
502
|
+
|
|
503
|
+
return result;
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
async deleteAllGlobal(tableName) {
|
|
507
|
+
return wrapper.deleteAllGlobal(this.gun, this.appName, tableName);
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
// ============================================================================
|
|
511
|
+
// REFERENCE OPERATIONS
|
|
512
|
+
// ============================================================================
|
|
513
|
+
|
|
514
|
+
/**
|
|
515
|
+
* Create a reference to data
|
|
516
|
+
*/
|
|
517
|
+
createReference(holon, lens, data) {
|
|
518
|
+
return this.references.createReference(holon, lens, data);
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
/**
|
|
522
|
+
* Check if data is a reference
|
|
523
|
+
*/
|
|
524
|
+
isReference(data) {
|
|
525
|
+
return this.references.isReference(data);
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
/**
|
|
529
|
+
* Resolve a reference to get actual data
|
|
530
|
+
*/
|
|
531
|
+
async resolveReference(reference, options = {}) {
|
|
532
|
+
return this.references.resolveReference(this.gun, reference, options);
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
/**
|
|
536
|
+
* Parse a soul path
|
|
537
|
+
*/
|
|
538
|
+
parseSoulPath(soul) {
|
|
539
|
+
return this.references.parseSoulPath(soul);
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
/**
|
|
543
|
+
* Get soul for data
|
|
544
|
+
*/
|
|
545
|
+
getSoul(holon, lens, key) {
|
|
546
|
+
return this.references.getSoul(holon, lens, key);
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
// ============================================================================
|
|
550
|
+
// AUTHENTICATION OPERATIONS
|
|
551
|
+
// ============================================================================
|
|
552
|
+
|
|
553
|
+
/**
|
|
554
|
+
* Authenticate for a holon
|
|
555
|
+
*/
|
|
556
|
+
async authenticate(holonId, password) {
|
|
557
|
+
return this.auth.authenticate(holonId, password);
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
/**
|
|
561
|
+
* Create a new user
|
|
562
|
+
*/
|
|
563
|
+
async createUser(holonId, password) {
|
|
564
|
+
return this.auth.createUser(holonId, password);
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
/**
|
|
568
|
+
* Logout current user
|
|
569
|
+
*/
|
|
570
|
+
logout() {
|
|
571
|
+
this.auth.logout();
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
/**
|
|
575
|
+
* Check if authenticated
|
|
576
|
+
*/
|
|
577
|
+
isAuthenticated() {
|
|
578
|
+
return this.auth.isAuthenticated();
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
/**
|
|
582
|
+
* Write to private storage
|
|
583
|
+
*/
|
|
584
|
+
async writePrivate(lens, key, data) {
|
|
585
|
+
return this.auth.writePrivate(lens, key, data);
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
/**
|
|
589
|
+
* Read from private storage
|
|
590
|
+
*/
|
|
591
|
+
async readPrivate(lens, key) {
|
|
592
|
+
return this.auth.readPrivate(lens, key);
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
/**
|
|
596
|
+
* Read all from private lens
|
|
597
|
+
*/
|
|
598
|
+
async readAllPrivate(lens, timeout = 2000) {
|
|
599
|
+
return this.auth.readAllPrivate(lens, timeout);
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
/**
|
|
603
|
+
* Delete from private storage
|
|
604
|
+
*/
|
|
605
|
+
async deletePrivate(lens, key) {
|
|
606
|
+
return this.auth.deletePrivate(lens, key);
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
// ============================================================================
|
|
610
|
+
// SCHEMA OPERATIONS
|
|
611
|
+
// ============================================================================
|
|
612
|
+
|
|
613
|
+
/**
|
|
614
|
+
* Set a schema for a lens
|
|
615
|
+
*/
|
|
616
|
+
async setSchema(lens, schema) {
|
|
617
|
+
return this.schemaValidator.setSchema(this.gun, this.appName, lens, schema);
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
/**
|
|
621
|
+
* Get a schema for a lens
|
|
622
|
+
*/
|
|
623
|
+
async getSchema(lens, options = {}) {
|
|
624
|
+
return this.schemaValidator.getSchema(this.gun, this.appName, lens, options);
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
/**
|
|
628
|
+
* Get all schemas
|
|
629
|
+
*/
|
|
630
|
+
async getAllSchemas() {
|
|
631
|
+
return this.schemaValidator.getAllSchemas(this.gun, this.appName);
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
/**
|
|
635
|
+
* Delete a schema
|
|
636
|
+
*/
|
|
637
|
+
async deleteSchema(lens) {
|
|
638
|
+
return this.schemaValidator.deleteSchema(this.gun, this.appName, lens);
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
/**
|
|
642
|
+
* Validate data against a schema
|
|
643
|
+
*/
|
|
644
|
+
validate(schema, data) {
|
|
645
|
+
return this.schemaValidator.validate(schema, data);
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
/**
|
|
649
|
+
* Validate data against a lens schema
|
|
650
|
+
*/
|
|
651
|
+
async validateData(lens, data) {
|
|
652
|
+
return this.schemaValidator.validateData(this.gun, this.appName, lens, data);
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
/**
|
|
656
|
+
* Clear schema cache
|
|
657
|
+
*/
|
|
658
|
+
clearSchemaCache(lens = null) {
|
|
659
|
+
this.schemaValidator.clearCache(lens);
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
// ============================================================================
|
|
663
|
+
// SUBSCRIPTIONS
|
|
664
|
+
// ============================================================================
|
|
665
|
+
|
|
101
666
|
async subscribe(path, callback, options = {}) {
|
|
102
|
-
|
|
667
|
+
const subscriptionId = ++this.subscriptionCounter;
|
|
103
668
|
const pathParts = path.split('/');
|
|
104
669
|
const isPrefix = pathParts.length <= 3;
|
|
105
670
|
|
|
671
|
+
const subscription = {
|
|
672
|
+
id: subscriptionId,
|
|
673
|
+
path,
|
|
674
|
+
active: true,
|
|
675
|
+
gunRef: null
|
|
676
|
+
};
|
|
677
|
+
|
|
106
678
|
if (isPrefix) {
|
|
107
|
-
// Subscribe to all items under this prefix
|
|
108
679
|
const ref = this.gun.get(path);
|
|
109
|
-
|
|
680
|
+
subscription.gunRef = ref;
|
|
681
|
+
|
|
682
|
+
ref.map().on(async (data, key) => {
|
|
683
|
+
if (!this.subscriptions.get(subscriptionId)?.active) return;
|
|
684
|
+
if (!data || key.startsWith('_') || data._deleted) return;
|
|
685
|
+
|
|
686
|
+
// Parse data
|
|
687
|
+
let parsed = wrapper.parse(data);
|
|
688
|
+
if (!parsed) return;
|
|
689
|
+
|
|
690
|
+
// Resolve references if requested
|
|
691
|
+
if (options.resolveReferences && this.isReference(parsed)) {
|
|
692
|
+
parsed = await this.resolveReference(parsed);
|
|
693
|
+
}
|
|
110
694
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
callback(data, key);
|
|
695
|
+
if (parsed) {
|
|
696
|
+
callback(parsed, key);
|
|
114
697
|
}
|
|
115
698
|
});
|
|
699
|
+
} else {
|
|
700
|
+
const ref = this.gun.get(path);
|
|
701
|
+
subscription.gunRef = ref;
|
|
116
702
|
|
|
117
|
-
|
|
118
|
-
|
|
703
|
+
ref.on(async (data, key) => {
|
|
704
|
+
if (!this.subscriptions.get(subscriptionId)?.active) return;
|
|
705
|
+
if (!data) return;
|
|
119
706
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
}
|
|
130
|
-
};
|
|
131
|
-
} else {
|
|
132
|
-
// Subscribe to single item
|
|
133
|
-
const unsubFn = wrapper.subscribe(this.gun, path, callback);
|
|
134
|
-
return {
|
|
135
|
-
unsubscribe: typeof unsubFn === 'function' ? unsubFn : () => {},
|
|
136
|
-
};
|
|
707
|
+
let parsed = wrapper.parse(data);
|
|
708
|
+
if (!parsed || parsed._deleted) return;
|
|
709
|
+
|
|
710
|
+
if (options.resolveReferences && this.isReference(parsed)) {
|
|
711
|
+
parsed = await this.resolveReference(parsed);
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
if (parsed) {
|
|
715
|
+
callback(parsed, key);
|
|
716
|
+
}
|
|
717
|
+
});
|
|
137
718
|
}
|
|
719
|
+
|
|
720
|
+
this.subscriptions.set(subscriptionId, subscription);
|
|
721
|
+
|
|
722
|
+
return {
|
|
723
|
+
id: subscriptionId,
|
|
724
|
+
unsubscribe: () => {
|
|
725
|
+
const sub = this.subscriptions.get(subscriptionId);
|
|
726
|
+
if (sub) {
|
|
727
|
+
sub.active = false;
|
|
728
|
+
if (sub.gunRef) {
|
|
729
|
+
try {
|
|
730
|
+
sub.gunRef.off();
|
|
731
|
+
} catch (e) {}
|
|
732
|
+
}
|
|
733
|
+
this.subscriptions.delete(subscriptionId);
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
};
|
|
138
737
|
}
|
|
139
738
|
|
|
739
|
+
// ============================================================================
|
|
740
|
+
// EXPORT/IMPORT
|
|
741
|
+
// ============================================================================
|
|
742
|
+
|
|
140
743
|
async exportData(pathPrefix = '') {
|
|
141
744
|
const records = [];
|
|
142
|
-
|
|
143
|
-
// Get the app-level data
|
|
144
|
-
const appName = this.config.appName || 'holosphere';
|
|
145
|
-
const basePath = pathPrefix || appName;
|
|
745
|
+
const basePath = pathPrefix || this.appName;
|
|
146
746
|
|
|
147
747
|
try {
|
|
148
748
|
const items = await gunMap(this.gun.get(basePath), 2000);
|
|
149
749
|
|
|
150
750
|
for (const [key, data] of Object.entries(items)) {
|
|
151
751
|
if (!key.startsWith('_') && data && typeof data === 'object' && !data._deleted) {
|
|
152
|
-
// Recursively collect nested data
|
|
153
752
|
await this._collectRecords(basePath, key, data, records);
|
|
154
753
|
}
|
|
155
754
|
}
|
|
@@ -164,14 +763,12 @@ export class GunDBBackend extends StorageBackend {
|
|
|
164
763
|
const currentPath = `${basePath}/${key}`;
|
|
165
764
|
|
|
166
765
|
if (data.id) {
|
|
167
|
-
// This is a leaf data node
|
|
168
766
|
records.push({
|
|
169
767
|
path: currentPath,
|
|
170
768
|
data: this._cleanGunData(data),
|
|
171
769
|
timestamp: data._meta?.timestamp || Date.now(),
|
|
172
770
|
});
|
|
173
771
|
} else {
|
|
174
|
-
// This might be a nested structure, explore further
|
|
175
772
|
try {
|
|
176
773
|
const nested = await gunMap(this.gun.get(currentPath), 500);
|
|
177
774
|
for (const [nestedKey, nestedData] of Object.entries(nested)) {
|
|
@@ -179,14 +776,11 @@ export class GunDBBackend extends StorageBackend {
|
|
|
179
776
|
await this._collectRecords(currentPath, nestedKey, nestedData, records);
|
|
180
777
|
}
|
|
181
778
|
}
|
|
182
|
-
} catch (e) {
|
|
183
|
-
// Ignore nested exploration errors
|
|
184
|
-
}
|
|
779
|
+
} catch (e) {}
|
|
185
780
|
}
|
|
186
781
|
}
|
|
187
782
|
|
|
188
783
|
_cleanGunData(data) {
|
|
189
|
-
// Remove Gun's internal metadata
|
|
190
784
|
const cleaned = { ...data };
|
|
191
785
|
delete cleaned['_'];
|
|
192
786
|
return cleaned;
|
|
@@ -208,14 +802,59 @@ export class GunDBBackend extends StorageBackend {
|
|
|
208
802
|
return results;
|
|
209
803
|
}
|
|
210
804
|
|
|
805
|
+
// ============================================================================
|
|
806
|
+
// LIFECYCLE
|
|
807
|
+
// ============================================================================
|
|
808
|
+
|
|
211
809
|
close() {
|
|
212
|
-
//
|
|
810
|
+
// 1. Unsubscribe all active subscriptions
|
|
811
|
+
for (const [id, subscription] of this.subscriptions) {
|
|
812
|
+
if (subscription.active) {
|
|
813
|
+
subscription.active = false;
|
|
814
|
+
if (subscription.gunRef) {
|
|
815
|
+
try {
|
|
816
|
+
subscription.gunRef.off();
|
|
817
|
+
} catch (e) {}
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
this.subscriptions.clear();
|
|
822
|
+
|
|
823
|
+
// 2. Clear schema cache
|
|
824
|
+
if (this.schemaValidator) {
|
|
825
|
+
this.schemaValidator.clearCache();
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
// 3. Clear write cache
|
|
829
|
+
this.writeCache.clear();
|
|
830
|
+
|
|
831
|
+
// 4. Logout auth
|
|
832
|
+
if (this.auth) {
|
|
833
|
+
this.auth.logout();
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
// 5. Clean up Gun connections
|
|
213
837
|
if (this.gun) {
|
|
214
838
|
try {
|
|
215
|
-
//
|
|
839
|
+
// Clean up mesh connections
|
|
840
|
+
const mesh = this.gun.back('opt.mesh');
|
|
841
|
+
if (mesh) {
|
|
842
|
+
if (mesh.way) {
|
|
843
|
+
Object.values(mesh.way).forEach(conn => {
|
|
844
|
+
if (conn?.wire?.close) conn.wire.close();
|
|
845
|
+
});
|
|
846
|
+
}
|
|
847
|
+
if (mesh.opt?.peers) mesh.opt.peers = {};
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
// Close web server if present
|
|
851
|
+
const server = this.gun.back('opt.web');
|
|
852
|
+
if (server?.close) server.close();
|
|
853
|
+
|
|
854
|
+
// Turn off all listeners
|
|
216
855
|
this.gun.off();
|
|
217
|
-
} catch (
|
|
218
|
-
|
|
856
|
+
} catch (error) {
|
|
857
|
+
console.warn('Error during Gun cleanup:', error);
|
|
219
858
|
}
|
|
220
859
|
}
|
|
221
860
|
}
|
|
@@ -226,6 +865,9 @@ export class GunDBBackend extends StorageBackend {
|
|
|
226
865
|
publicKey: this.publicKey,
|
|
227
866
|
peers: this.config.peers || [],
|
|
228
867
|
connected: !!this.gun,
|
|
868
|
+
authenticated: this.isAuthenticated(),
|
|
869
|
+
subscriptionCount: this.subscriptions.size,
|
|
870
|
+
schemaValidationEnabled: this.schemaValidator?.isStrict() || false
|
|
229
871
|
};
|
|
230
872
|
}
|
|
231
873
|
}
|