holosphere 2.0.0-alpha1 → 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.
Files changed (418) hide show
  1. package/CHANGELOG.md +473 -0
  2. package/FEATURES.md +431 -0
  3. package/LICENSE +29 -166
  4. package/LICENSE-AGPL.md +180 -0
  5. package/README.md +97 -16
  6. package/dist/2019-D2OG2idw.js +6680 -0
  7. package/dist/2019-D2OG2idw.js.map +1 -0
  8. package/dist/2019-EION3wKo.cjs +8 -0
  9. package/dist/2019-EION3wKo.cjs.map +1 -0
  10. package/dist/_commonjsHelpers-C37NGDzP.cjs +2 -0
  11. package/dist/_commonjsHelpers-C37NGDzP.cjs.map +1 -0
  12. package/dist/_commonjsHelpers-CUmg6egw.js +7 -0
  13. package/dist/_commonjsHelpers-CUmg6egw.js.map +1 -0
  14. package/dist/browser-BSniCNqO.js +3058 -0
  15. package/dist/browser-BSniCNqO.js.map +1 -0
  16. package/dist/browser-Cq59Ij19.cjs +2 -0
  17. package/dist/browser-Cq59Ij19.cjs.map +1 -0
  18. package/dist/cdn/holosphere.min.js +55 -0
  19. package/dist/cdn/holosphere.min.js.map +1 -0
  20. package/dist/cjs/holosphere.cjs +2 -0
  21. package/dist/cjs/holosphere.cjs.map +1 -0
  22. package/dist/esm/holosphere.js +53 -0
  23. package/dist/esm/holosphere.js.map +1 -0
  24. package/dist/index-DDGt_V9o.cjs +12 -0
  25. package/dist/index-DDGt_V9o.cjs.map +1 -0
  26. package/dist/index-DJXftyvB.js +39841 -0
  27. package/dist/index-DJXftyvB.js.map +1 -0
  28. package/dist/index-DMbdcMtK.cjs +18 -0
  29. package/dist/index-DMbdcMtK.cjs.map +1 -0
  30. package/dist/index-DeZ1xz_s.js +15104 -0
  31. package/dist/index-DeZ1xz_s.js.map +1 -0
  32. package/dist/indexeddb-storage-BFt6hMeF.js +176 -0
  33. package/dist/indexeddb-storage-BFt6hMeF.js.map +1 -0
  34. package/dist/indexeddb-storage-BK5tv4Sh.cjs +2 -0
  35. package/dist/indexeddb-storage-BK5tv4Sh.cjs.map +1 -0
  36. package/dist/memory-storage-C9HuoL2E.js +91 -0
  37. package/dist/memory-storage-C9HuoL2E.js.map +1 -0
  38. package/dist/memory-storage-Dao7jfYG.cjs +2 -0
  39. package/dist/memory-storage-Dao7jfYG.cjs.map +1 -0
  40. package/dist/secp256k1-BbKzbLtD.cjs +12 -0
  41. package/dist/secp256k1-BbKzbLtD.cjs.map +1 -0
  42. package/dist/secp256k1-CreY7Pcl.js +1890 -0
  43. package/dist/secp256k1-CreY7Pcl.js.map +1 -0
  44. package/docs/CONTRACTS.md +797 -0
  45. package/docs/FOSDEM_PROPOSAL.md +388 -0
  46. package/docs/LOCALFIRST.md +266 -0
  47. package/docs/api/ai_aggregation.js.html +333 -0
  48. package/docs/api/ai_breakdown.js.html +524 -0
  49. package/docs/api/ai_classifier.js.html +231 -0
  50. package/docs/api/ai_council.js.html +246 -0
  51. package/docs/api/ai_embeddings.js.html +304 -0
  52. package/docs/api/ai_federation-ai.js.html +338 -0
  53. package/docs/api/ai_h3-ai.js.html +970 -0
  54. package/docs/api/ai_index.js.html +124 -0
  55. package/docs/api/ai_json-ops.js.html +241 -0
  56. package/docs/api/ai_llm-service.js.html +239 -0
  57. package/docs/api/ai_nl-query.js.html +236 -0
  58. package/docs/api/ai_relationships.js.html +367 -0
  59. package/docs/api/ai_schema-extractor.js.html +235 -0
  60. package/docs/api/ai_spatial.js.html +307 -0
  61. package/docs/api/ai_tts.js.html +214 -0
  62. package/docs/api/content_social-protocols.js.html +180 -0
  63. package/docs/api/core_holosphere.js.html +757 -0
  64. package/docs/api/crypto_nostr-utils.js.html +306 -0
  65. package/docs/api/crypto_secp256k1.js.html +267 -0
  66. package/docs/api/data/search.json +1 -0
  67. package/docs/api/federation_discovery.js.html +337 -0
  68. package/docs/api/federation_handshake.js.html +478 -0
  69. package/docs/api/federation_hologram.js.html +1053 -0
  70. package/docs/api/federation_registry.js.html +389 -0
  71. package/docs/api/fonts/Inconsolata-Regular.ttf +0 -0
  72. package/docs/api/fonts/OpenSans-Regular.ttf +0 -0
  73. package/docs/api/fonts/WorkSans-Bold.ttf +0 -0
  74. package/docs/api/global.html +3 -0
  75. package/docs/api/hierarchical_upcast.js.html +128 -0
  76. package/docs/api/index.html +265 -0
  77. package/docs/api/index.js.html +1868 -0
  78. package/docs/api/lib_ai-methods.js.html +660 -0
  79. package/docs/api/lib_contract-methods.js.html +445 -0
  80. package/docs/api/lib_errors.js.html +56 -0
  81. package/docs/api/lib_federation-methods.js.html +348 -0
  82. package/docs/api/lib_index.js.html +33 -0
  83. package/docs/api/module-ai.html +5 -0
  84. package/docs/api/module-ai_aggregation-SmartAggregation.html +6 -0
  85. package/docs/api/module-ai_aggregation.SmartAggregation.html +3 -0
  86. package/docs/api/module-ai_aggregation.html +3 -0
  87. package/docs/api/module-ai_breakdown-TaskBreakdown.html +5 -0
  88. package/docs/api/module-ai_breakdown.TaskBreakdown.html +3 -0
  89. package/docs/api/module-ai_breakdown.html +3 -0
  90. package/docs/api/module-ai_classifier-Classifier.html +6 -0
  91. package/docs/api/module-ai_classifier.Classifier.html +3 -0
  92. package/docs/api/module-ai_classifier.html +3 -0
  93. package/docs/api/module-ai_council-Council.html +6 -0
  94. package/docs/api/module-ai_council.Council.html +3 -0
  95. package/docs/api/module-ai_council.html +3 -0
  96. package/docs/api/module-ai_embeddings-Embeddings.html +5 -0
  97. package/docs/api/module-ai_embeddings.Embeddings.html +3 -0
  98. package/docs/api/module-ai_embeddings.html +3 -0
  99. package/docs/api/module-ai_federation-ai-FederationAdvisor.html +6 -0
  100. package/docs/api/module-ai_federation-ai.FederationAdvisor.html +3 -0
  101. package/docs/api/module-ai_federation-ai.html +3 -0
  102. package/docs/api/module-ai_h3-ai-H3AI.html +6 -0
  103. package/docs/api/module-ai_h3-ai.H3AI.html +3 -0
  104. package/docs/api/module-ai_h3-ai.html +3 -0
  105. package/docs/api/module-ai_json-ops-JSONOps.html +5 -0
  106. package/docs/api/module-ai_json-ops.JSONOps.html +3 -0
  107. package/docs/api/module-ai_json-ops.html +3 -0
  108. package/docs/api/module-ai_llm-service-LLMService.html +5 -0
  109. package/docs/api/module-ai_llm-service.LLMService.html +3 -0
  110. package/docs/api/module-ai_llm-service.html +3 -0
  111. package/docs/api/module-ai_nl-query-NLQuery.html +5 -0
  112. package/docs/api/module-ai_nl-query.NLQuery.html +3 -0
  113. package/docs/api/module-ai_nl-query.html +3 -0
  114. package/docs/api/module-ai_relationships-RelationshipDiscovery.html +6 -0
  115. package/docs/api/module-ai_relationships.RelationshipDiscovery.html +3 -0
  116. package/docs/api/module-ai_relationships.html +3 -0
  117. package/docs/api/module-ai_schema-extractor-SchemaExtractor.html +5 -0
  118. package/docs/api/module-ai_schema-extractor.SchemaExtractor.html +3 -0
  119. package/docs/api/module-ai_schema-extractor.html +3 -0
  120. package/docs/api/module-ai_spatial-SpatialAnalysis.html +6 -0
  121. package/docs/api/module-ai_spatial.SpatialAnalysis.html +3 -0
  122. package/docs/api/module-ai_spatial.html +3 -0
  123. package/docs/api/module-ai_tts-TTS.html +5 -0
  124. package/docs/api/module-ai_tts.TTS.html +3 -0
  125. package/docs/api/module-ai_tts.html +3 -0
  126. package/docs/api/module-content_social-protocols.html +3 -0
  127. package/docs/api/module-core_holosphere-HoloSphere.html +6 -0
  128. package/docs/api/module-core_holosphere.HoloSphere.html +3 -0
  129. package/docs/api/module-core_holosphere.html +3 -0
  130. package/docs/api/module-crypto_nostr-utils.html +3 -0
  131. package/docs/api/module-crypto_secp256k1.html +3 -0
  132. package/docs/api/module-federation_hologram.html +3 -0
  133. package/docs/api/module-hierarchical_upcast.html +3 -0
  134. package/docs/api/module-holosphere-HoloSphereBase.html +3 -0
  135. package/docs/api/module-holosphere.html +3 -0
  136. package/docs/api/module-lib_ai-methods.html +3 -0
  137. package/docs/api/module-lib_contract-methods.html +3 -0
  138. package/docs/api/module-lib_errors-AuthorizationError.html +3 -0
  139. package/docs/api/module-lib_errors-ValidationError.html +3 -0
  140. package/docs/api/module-lib_errors.AuthorizationError.html +3 -0
  141. package/docs/api/module-lib_errors.ValidationError.html +3 -0
  142. package/docs/api/module-lib_errors.html +3 -0
  143. package/docs/api/module-lib_federation-methods.html +3 -0
  144. package/docs/api/module-lib_index.html +3 -0
  145. package/docs/api/module-schema_validator-ValidationError.html +3 -0
  146. package/docs/api/module-schema_validator.ValidationError.html +3 -0
  147. package/docs/api/module-schema_validator.html +3 -0
  148. package/docs/api/module-spatial_h3-operations.html +4 -0
  149. package/docs/api/module-storage_backend-factory.BackendFactory.html +3 -0
  150. package/docs/api/module-storage_backend-factory.html +3 -0
  151. package/docs/api/module-storage_backend-interface-StorageBackend.html +3 -0
  152. package/docs/api/module-storage_backend-interface.StorageBackend.html +3 -0
  153. package/docs/api/module-storage_backend-interface.html +3 -0
  154. package/docs/api/module-storage_backends_activitypub-backend-ActivityPubBackend.html +7 -0
  155. package/docs/api/module-storage_backends_activitypub-backend.ActivityPubBackend.html +3 -0
  156. package/docs/api/module-storage_backends_activitypub-backend.html +3 -0
  157. package/docs/api/module-storage_backends_activitypub_server-ActivityPubServer.html +8 -0
  158. package/docs/api/module-storage_backends_activitypub_server.ActivityPubServer.html +3 -0
  159. package/docs/api/module-storage_backends_activitypub_server.html +3 -0
  160. package/docs/api/module-storage_backends_gundb-backend-GunDBBackend.html +7 -0
  161. package/docs/api/module-storage_backends_gundb-backend.GunDBBackend.html +3 -0
  162. package/docs/api/module-storage_backends_gundb-backend.html +3 -0
  163. package/docs/api/module-storage_backends_nostr-backend-NostrBackend.html +8 -0
  164. package/docs/api/module-storage_backends_nostr-backend.NostrBackend.html +3 -0
  165. package/docs/api/module-storage_backends_nostr-backend.html +3 -0
  166. package/docs/api/module-storage_filesystem-storage-FileSystemStorage.html +5 -0
  167. package/docs/api/module-storage_filesystem-storage-browser-FileSystemStorage.html +3 -0
  168. package/docs/api/module-storage_filesystem-storage-browser.FileSystemStorage.html +3 -0
  169. package/docs/api/module-storage_filesystem-storage-browser.html +3 -0
  170. package/docs/api/module-storage_filesystem-storage.FileSystemStorage.html +3 -0
  171. package/docs/api/module-storage_filesystem-storage.html +3 -0
  172. package/docs/api/module-storage_global-tables.html +3 -0
  173. package/docs/api/module-storage_gun-async.html +3 -0
  174. package/docs/api/module-storage_gun-auth-GunAuth.html +5 -0
  175. package/docs/api/module-storage_gun-auth.GunAuth.html +3 -0
  176. package/docs/api/module-storage_gun-auth.html +3 -0
  177. package/docs/api/module-storage_gun-federation.html +3 -0
  178. package/docs/api/module-storage_gun-references-GunReferenceHandler.html +5 -0
  179. package/docs/api/module-storage_gun-references.GunReferenceHandler.html +3 -0
  180. package/docs/api/module-storage_gun-references.html +3 -0
  181. package/docs/api/module-storage_gun-schema-GunSchemaValidator.html +5 -0
  182. package/docs/api/module-storage_gun-schema.GunSchemaValidator.html +3 -0
  183. package/docs/api/module-storage_gun-schema.html +3 -0
  184. package/docs/api/module-storage_gun-wrapper.html +11 -0
  185. package/docs/api/module-storage_indexeddb-storage-IndexedDBStorage.html +5 -0
  186. package/docs/api/module-storage_indexeddb-storage.IndexedDBStorage.html +3 -0
  187. package/docs/api/module-storage_indexeddb-storage.html +3 -0
  188. package/docs/api/module-storage_key-storage-simple.html +3 -0
  189. package/docs/api/module-storage_key-storage.html +4 -0
  190. package/docs/api/module-storage_memory-storage-MemoryStorage.html +5 -0
  191. package/docs/api/module-storage_memory-storage.MemoryStorage.html +3 -0
  192. package/docs/api/module-storage_memory-storage.html +3 -0
  193. package/docs/api/module-storage_migration-MigrationTool.html +6 -0
  194. package/docs/api/module-storage_migration.MigrationTool.html +3 -0
  195. package/docs/api/module-storage_migration.html +3 -0
  196. package/docs/api/module-storage_nostr-async.html +18 -0
  197. package/docs/api/module-storage_nostr-client-LRUCache.html +3 -0
  198. package/docs/api/module-storage_nostr-client-NostrClient.html +7 -0
  199. package/docs/api/module-storage_nostr-client.NostrClient.html +15 -0
  200. package/docs/api/module-storage_nostr-client.html +6 -0
  201. package/docs/api/module-storage_nostr-wrapper.html +3 -0
  202. package/docs/api/module-storage_outbox-queue-OutboxQueue.html +4 -0
  203. package/docs/api/module-storage_outbox-queue.OutboxQueue.html +3 -0
  204. package/docs/api/module-storage_outbox-queue.html +3 -0
  205. package/docs/api/module-storage_persistent-storage-PersistentStorage.html +3 -0
  206. package/docs/api/module-storage_persistent-storage.html +4 -0
  207. package/docs/api/module-storage_sync-service-SyncService.html +5 -0
  208. package/docs/api/module-storage_sync-service.SyncService.html +3 -0
  209. package/docs/api/module-storage_sync-service.html +3 -0
  210. package/docs/api/module-storage_unified-storage.html +3 -0
  211. package/docs/api/module-subscriptions_manager.SubscriptionRegistry.html +3 -0
  212. package/docs/api/module-subscriptions_manager.html +3 -0
  213. package/docs/api/schema_validator.js.html +113 -0
  214. package/docs/api/scripts/core.js +726 -0
  215. package/docs/api/scripts/core.min.js +23 -0
  216. package/docs/api/scripts/resize.js +90 -0
  217. package/docs/api/scripts/search.js +265 -0
  218. package/docs/api/scripts/search.min.js +6 -0
  219. package/docs/api/scripts/third-party/Apache-License-2.0.txt +202 -0
  220. package/docs/api/scripts/third-party/fuse.js +9 -0
  221. package/docs/api/scripts/third-party/hljs-line-num-original.js +369 -0
  222. package/docs/api/scripts/third-party/hljs-line-num.js +1 -0
  223. package/docs/api/scripts/third-party/hljs-original.js +5171 -0
  224. package/docs/api/scripts/third-party/hljs.js +1 -0
  225. package/docs/api/scripts/third-party/popper.js +5 -0
  226. package/docs/api/scripts/third-party/tippy.js +1 -0
  227. package/docs/api/scripts/third-party/tocbot.js +672 -0
  228. package/docs/api/scripts/third-party/tocbot.min.js +1 -0
  229. package/docs/api/spatial_h3-operations.js.html +129 -0
  230. package/docs/api/storage_backend-factory.js.html +133 -0
  231. package/docs/api/storage_backend-interface.js.html +164 -0
  232. package/docs/api/storage_backends_activitypub-backend.js.html +298 -0
  233. package/docs/api/storage_backends_activitypub_server.js.html +678 -0
  234. package/docs/api/storage_backends_gundb-backend.js.html +878 -0
  235. package/docs/api/storage_backends_nostr-backend.js.html +254 -0
  236. package/docs/api/storage_filesystem-storage-browser.js.html +83 -0
  237. package/docs/api/storage_filesystem-storage.js.html +207 -0
  238. package/docs/api/storage_global-tables.js.html +116 -0
  239. package/docs/api/storage_gun-async.js.html +344 -0
  240. package/docs/api/storage_gun-auth.js.html +376 -0
  241. package/docs/api/storage_gun-federation.js.html +788 -0
  242. package/docs/api/storage_gun-references.js.html +212 -0
  243. package/docs/api/storage_gun-schema.js.html +309 -0
  244. package/docs/api/storage_gun-wrapper.js.html +645 -0
  245. package/docs/api/storage_indexeddb-storage.js.html +224 -0
  246. package/docs/api/storage_key-storage-simple.js.html +102 -0
  247. package/docs/api/storage_key-storage.js.html +171 -0
  248. package/docs/api/storage_memory-storage.js.html +128 -0
  249. package/docs/api/storage_migration.js.html +354 -0
  250. package/docs/api/storage_nostr-async.js.html +1076 -0
  251. package/docs/api/storage_nostr-client.js.html +1598 -0
  252. package/docs/api/storage_nostr-wrapper.js.html +218 -0
  253. package/docs/api/storage_outbox-queue.js.html +248 -0
  254. package/docs/api/storage_persistent-storage.js.html +160 -0
  255. package/docs/api/storage_sync-service.js.html +201 -0
  256. package/docs/api/storage_unified-storage.js.html +157 -0
  257. package/docs/api/styles/clean-jsdoc-theme-base.css +1159 -0
  258. package/docs/api/styles/clean-jsdoc-theme-dark.css +412 -0
  259. package/docs/api/styles/clean-jsdoc-theme-light.css +482 -0
  260. package/docs/api/styles/clean-jsdoc-theme-scrollbar.css +30 -0
  261. package/docs/api/styles/clean-jsdoc-theme-without-scrollbar.min.css +1 -0
  262. package/docs/api/styles/clean-jsdoc-theme.min.css +1 -0
  263. package/docs/api/subscriptions_manager.js.html +162 -0
  264. package/docs/contracts/api-interface.md +793 -0
  265. package/docs/data-model.md +476 -0
  266. package/docs/gun-async-usage.md +338 -0
  267. package/docs/plan.md +349 -0
  268. package/docs/quickstart.md +674 -0
  269. package/docs/research.md +362 -0
  270. package/docs/spec.md +244 -0
  271. package/docs/storage-backends.md +326 -0
  272. package/docs/tasks.md +947 -0
  273. package/examples/demo.html +47 -0
  274. package/examples/holosphere-widget.js +1242 -0
  275. package/examples/widget-demo.html +274 -0
  276. package/examples/widget.html +703 -0
  277. package/jsdoc.json +26 -0
  278. package/package.json +25 -7
  279. package/src/ai/aggregation.js +13 -2
  280. package/src/ai/breakdown.js +12 -2
  281. package/src/ai/classifier.js +14 -3
  282. package/src/ai/council.js +22 -7
  283. package/src/ai/embeddings.js +37 -15
  284. package/src/ai/federation-ai.js +13 -2
  285. package/src/ai/h3-ai.js +14 -2
  286. package/src/ai/index.js +16 -7
  287. package/src/ai/json-ops.js +18 -5
  288. package/src/ai/llm-service.js +62 -31
  289. package/src/ai/nl-query.js +12 -2
  290. package/src/ai/relationships.js +13 -2
  291. package/src/ai/schema-extractor.js +24 -10
  292. package/src/ai/spatial.js +13 -2
  293. package/src/ai/tts.js +25 -8
  294. package/src/cdn-entry.js +22 -0
  295. package/src/content/social-protocols.js +34 -25
  296. package/src/contracts/abis/Appreciative.json +1280 -0
  297. package/src/contracts/abis/AppreciativeFactory.json +101 -0
  298. package/src/contracts/abis/Bundle.json +1438 -0
  299. package/src/contracts/abis/BundleFactory.json +106 -0
  300. package/src/contracts/abis/Holon.json +881 -0
  301. package/src/contracts/abis/Holons.json +330 -0
  302. package/src/contracts/abis/Managed.json +1262 -0
  303. package/src/contracts/abis/ManagedFactory.json +149 -0
  304. package/src/contracts/abis/Membrane.json +261 -0
  305. package/src/contracts/abis/Splitter.json +1624 -0
  306. package/src/contracts/abis/SplitterFactory.json +220 -0
  307. package/src/contracts/abis/TestToken.json +321 -0
  308. package/src/contracts/abis/Zoned.json +1461 -0
  309. package/src/contracts/abis/ZonedFactory.json +154 -0
  310. package/src/contracts/chain-manager.js +403 -0
  311. package/src/contracts/deployer.js +500 -0
  312. package/src/contracts/event-listener.js +539 -0
  313. package/src/contracts/holon-contracts.js +359 -0
  314. package/src/contracts/index.js +82 -0
  315. package/src/contracts/networks.js +229 -0
  316. package/src/contracts/operations.js +687 -0
  317. package/src/contracts/queries.js +638 -0
  318. package/src/core/holosphere.js +487 -6
  319. package/src/crypto/nostr-utils.js +303 -0
  320. package/src/crypto/secp256k1.js +7 -2
  321. package/src/federation/handshake.js +475 -0
  322. package/src/federation/hologram.js +117 -3
  323. package/src/hierarchical/upcast.js +40 -25
  324. package/src/index.js +1501 -1909
  325. package/src/lib/ai-methods.js +657 -0
  326. package/src/lib/contract-methods.js +442 -0
  327. package/src/lib/errors.js +53 -0
  328. package/src/lib/federation-methods.js +345 -0
  329. package/src/lib/index.js +30 -0
  330. package/src/schema/validator.js +22 -3
  331. package/src/spatial/h3-operations.js +19 -3
  332. package/src/storage/backend-factory.js +7 -2
  333. package/src/storage/backend-interface.js +21 -2
  334. package/src/storage/backends/activitypub/server.js +25 -3
  335. package/src/storage/backends/activitypub-backend.js +25 -2
  336. package/src/storage/backends/gundb-backend.js +692 -50
  337. package/src/storage/backends/nostr-backend.js +116 -1
  338. package/src/storage/filesystem-storage-browser.js +42 -2
  339. package/src/storage/filesystem-storage.js +72 -5
  340. package/src/storage/global-tables.js +35 -3
  341. package/src/storage/gun-async.js +75 -15
  342. package/src/storage/gun-auth.js +373 -0
  343. package/src/storage/gun-federation.js +785 -0
  344. package/src/storage/gun-references.js +209 -0
  345. package/src/storage/gun-schema.js +306 -0
  346. package/src/storage/gun-wrapper.js +475 -54
  347. package/src/storage/indexeddb-storage.js +112 -13
  348. package/src/storage/key-storage-simple.js +32 -9
  349. package/src/storage/key-storage.js +45 -13
  350. package/src/storage/memory-storage.js +68 -2
  351. package/src/storage/migration.js +20 -7
  352. package/src/storage/nostr-async.js +412 -122
  353. package/src/storage/nostr-client.js +749 -76
  354. package/src/storage/nostr-wrapper.js +6 -2
  355. package/src/storage/outbox-queue.js +55 -18
  356. package/src/storage/persistent-storage.js +62 -14
  357. package/src/storage/sync-service.js +51 -17
  358. package/src/storage/unified-storage.js +154 -0
  359. package/src/subscriptions/manager.js +34 -17
  360. package/types/index.d.ts +133 -0
  361. package/vite.config.cdn.js +60 -0
  362. package/tests/unit/ai/aggregation.test.js +0 -295
  363. package/tests/unit/ai/breakdown.test.js +0 -446
  364. package/tests/unit/ai/classifier.test.js +0 -294
  365. package/tests/unit/ai/council.test.js +0 -262
  366. package/tests/unit/ai/embeddings.test.js +0 -384
  367. package/tests/unit/ai/federation-ai.test.js +0 -344
  368. package/tests/unit/ai/h3-ai.test.js +0 -458
  369. package/tests/unit/ai/index.test.js +0 -304
  370. package/tests/unit/ai/json-ops.test.js +0 -307
  371. package/tests/unit/ai/llm-service.test.js +0 -390
  372. package/tests/unit/ai/nl-query.test.js +0 -383
  373. package/tests/unit/ai/relationships.test.js +0 -311
  374. package/tests/unit/ai/schema-extractor.test.js +0 -384
  375. package/tests/unit/ai/spatial.test.js +0 -279
  376. package/tests/unit/ai/tts.test.js +0 -279
  377. package/tests/unit/content.test.js +0 -332
  378. package/tests/unit/contract/core.test.js +0 -88
  379. package/tests/unit/contract/crypto.test.js +0 -198
  380. package/tests/unit/contract/data.test.js +0 -223
  381. package/tests/unit/contract/federation.test.js +0 -181
  382. package/tests/unit/contract/hierarchical.test.js +0 -113
  383. package/tests/unit/contract/schema.test.js +0 -114
  384. package/tests/unit/contract/social.test.js +0 -217
  385. package/tests/unit/contract/spatial.test.js +0 -110
  386. package/tests/unit/contract/subscriptions.test.js +0 -128
  387. package/tests/unit/contract/utils.test.js +0 -159
  388. package/tests/unit/core.test.js +0 -152
  389. package/tests/unit/crypto.test.js +0 -328
  390. package/tests/unit/federation.test.js +0 -234
  391. package/tests/unit/gun-async.test.js +0 -252
  392. package/tests/unit/hierarchical.test.js +0 -399
  393. package/tests/unit/integration/scenario-01-geographic-storage.test.js +0 -74
  394. package/tests/unit/integration/scenario-02-federation.test.js +0 -76
  395. package/tests/unit/integration/scenario-03-subscriptions.test.js +0 -102
  396. package/tests/unit/integration/scenario-04-validation.test.js +0 -129
  397. package/tests/unit/integration/scenario-05-hierarchy.test.js +0 -125
  398. package/tests/unit/integration/scenario-06-social.test.js +0 -135
  399. package/tests/unit/integration/scenario-07-persistence.test.js +0 -130
  400. package/tests/unit/integration/scenario-08-authorization.test.js +0 -161
  401. package/tests/unit/integration/scenario-09-cross-dimensional.test.js +0 -139
  402. package/tests/unit/integration/scenario-10-cross-holosphere-capabilities.test.js +0 -357
  403. package/tests/unit/integration/scenario-11-cross-holosphere-federation.test.js +0 -410
  404. package/tests/unit/integration/scenario-12-capability-federated-read.test.js +0 -719
  405. package/tests/unit/performance/benchmark.test.js +0 -85
  406. package/tests/unit/schema.test.js +0 -213
  407. package/tests/unit/spatial.test.js +0 -158
  408. package/tests/unit/storage.test.js +0 -195
  409. package/tests/unit/subscriptions.test.js +0 -328
  410. package/tests/unit/test-data-permanence-debug.js +0 -197
  411. package/tests/unit/test-data-permanence.js +0 -340
  412. package/tests/unit/test-key-persistence-fixed.js +0 -148
  413. package/tests/unit/test-key-persistence.js +0 -172
  414. package/tests/unit/test-relay-permanence.js +0 -376
  415. package/tests/unit/test-second-node.js +0 -95
  416. package/tests/unit/test-simple-write.js +0 -89
  417. /package/{cleanup-test-data.js → scripts/cleanup-test-data.js} +0 -0
  418. /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
+ };