holosphere 2.0.0-alpha7 → 2.0.0-alpha9

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 (327) hide show
  1. package/CHANGELOG.md +446 -0
  2. package/FEATURES.md +431 -0
  3. package/LICENSE +29 -166
  4. package/LICENSE-AGPL.md +180 -0
  5. package/dist/cdn/holosphere.min.js +55 -0
  6. package/dist/cdn/holosphere.min.js.map +1 -0
  7. package/dist/cjs/holosphere.cjs +1 -1
  8. package/dist/esm/holosphere.js +1 -1
  9. package/dist/{index-C-IlLYlk.cjs → index-DDGt_V9o.cjs} +2 -2
  10. package/dist/{index-C-IlLYlk.cjs.map → index-DDGt_V9o.cjs.map} +1 -1
  11. package/dist/{index-d6f4RJBM.js → index-DJXftyvB.js} +2253 -387
  12. package/dist/index-DJXftyvB.js.map +1 -0
  13. package/dist/index-DMbdcMtK.cjs +18 -0
  14. package/dist/index-DMbdcMtK.cjs.map +1 -0
  15. package/dist/{index-jmTHEbR2.js → index-DeZ1xz_s.js} +2 -2
  16. package/dist/{index-jmTHEbR2.js.map → index-DeZ1xz_s.js.map} +1 -1
  17. package/dist/{indexeddb-storage-D8kOl0oK.js → indexeddb-storage-BFt6hMeF.js} +48 -4
  18. package/dist/indexeddb-storage-BFt6hMeF.js.map +1 -0
  19. package/dist/{indexeddb-storage-a8GipaDr.cjs → indexeddb-storage-BK5tv4Sh.cjs} +2 -2
  20. package/dist/indexeddb-storage-BK5tv4Sh.cjs.map +1 -0
  21. package/dist/{memory-storage-DBQK622V.js → memory-storage-C9HuoL2E.js} +44 -4
  22. package/dist/memory-storage-C9HuoL2E.js.map +1 -0
  23. package/dist/{memory-storage-gfRovk2O.cjs → memory-storage-Dao7jfYG.cjs} +2 -2
  24. package/dist/memory-storage-Dao7jfYG.cjs.map +1 -0
  25. package/dist/{secp256k1-BCAPF45D.cjs → secp256k1-BbKzbLtD.cjs} +2 -2
  26. package/dist/{secp256k1-BCAPF45D.cjs.map → secp256k1-BbKzbLtD.cjs.map} +1 -1
  27. package/dist/{secp256k1-DYm_CMqW.js → secp256k1-CreY7Pcl.js} +2 -2
  28. package/dist/{secp256k1-DYm_CMqW.js.map → secp256k1-CreY7Pcl.js.map} +1 -1
  29. package/docs/api/ai_aggregation.js.html +333 -0
  30. package/docs/api/ai_breakdown.js.html +524 -0
  31. package/docs/api/ai_classifier.js.html +231 -0
  32. package/docs/api/ai_council.js.html +246 -0
  33. package/docs/api/ai_embeddings.js.html +304 -0
  34. package/docs/api/ai_federation-ai.js.html +338 -0
  35. package/docs/api/ai_h3-ai.js.html +970 -0
  36. package/docs/api/ai_index.js.html +124 -0
  37. package/docs/api/ai_json-ops.js.html +241 -0
  38. package/docs/api/ai_llm-service.js.html +239 -0
  39. package/docs/api/ai_nl-query.js.html +236 -0
  40. package/docs/api/ai_relationships.js.html +367 -0
  41. package/docs/api/ai_schema-extractor.js.html +235 -0
  42. package/docs/api/ai_spatial.js.html +307 -0
  43. package/docs/api/ai_tts.js.html +214 -0
  44. package/docs/api/content_social-protocols.js.html +180 -0
  45. package/docs/api/core_holosphere.js.html +757 -0
  46. package/docs/api/crypto_nostr-utils.js.html +306 -0
  47. package/docs/api/crypto_secp256k1.js.html +267 -0
  48. package/docs/api/data/search.json +1 -0
  49. package/docs/api/federation_discovery.js.html +337 -0
  50. package/docs/api/federation_handshake.js.html +478 -0
  51. package/docs/api/federation_hologram.js.html +1053 -0
  52. package/docs/api/federation_registry.js.html +389 -0
  53. package/docs/api/fonts/Inconsolata-Regular.ttf +0 -0
  54. package/docs/api/fonts/OpenSans-Regular.ttf +0 -0
  55. package/docs/api/fonts/WorkSans-Bold.ttf +0 -0
  56. package/docs/api/global.html +3 -0
  57. package/docs/api/hierarchical_upcast.js.html +128 -0
  58. package/docs/api/index.html +265 -0
  59. package/docs/api/index.js.html +1868 -0
  60. package/docs/api/lib_ai-methods.js.html +660 -0
  61. package/docs/api/lib_contract-methods.js.html +445 -0
  62. package/docs/api/lib_errors.js.html +56 -0
  63. package/docs/api/lib_federation-methods.js.html +348 -0
  64. package/docs/api/lib_index.js.html +33 -0
  65. package/docs/api/module-ai.html +5 -0
  66. package/docs/api/module-ai_aggregation-SmartAggregation.html +6 -0
  67. package/docs/api/module-ai_aggregation.SmartAggregation.html +3 -0
  68. package/docs/api/module-ai_aggregation.html +3 -0
  69. package/docs/api/module-ai_breakdown-TaskBreakdown.html +5 -0
  70. package/docs/api/module-ai_breakdown.TaskBreakdown.html +3 -0
  71. package/docs/api/module-ai_breakdown.html +3 -0
  72. package/docs/api/module-ai_classifier-Classifier.html +6 -0
  73. package/docs/api/module-ai_classifier.Classifier.html +3 -0
  74. package/docs/api/module-ai_classifier.html +3 -0
  75. package/docs/api/module-ai_council-Council.html +6 -0
  76. package/docs/api/module-ai_council.Council.html +3 -0
  77. package/docs/api/module-ai_council.html +3 -0
  78. package/docs/api/module-ai_embeddings-Embeddings.html +5 -0
  79. package/docs/api/module-ai_embeddings.Embeddings.html +3 -0
  80. package/docs/api/module-ai_embeddings.html +3 -0
  81. package/docs/api/module-ai_federation-ai-FederationAdvisor.html +6 -0
  82. package/docs/api/module-ai_federation-ai.FederationAdvisor.html +3 -0
  83. package/docs/api/module-ai_federation-ai.html +3 -0
  84. package/docs/api/module-ai_h3-ai-H3AI.html +6 -0
  85. package/docs/api/module-ai_h3-ai.H3AI.html +3 -0
  86. package/docs/api/module-ai_h3-ai.html +3 -0
  87. package/docs/api/module-ai_json-ops-JSONOps.html +5 -0
  88. package/docs/api/module-ai_json-ops.JSONOps.html +3 -0
  89. package/docs/api/module-ai_json-ops.html +3 -0
  90. package/docs/api/module-ai_llm-service-LLMService.html +5 -0
  91. package/docs/api/module-ai_llm-service.LLMService.html +3 -0
  92. package/docs/api/module-ai_llm-service.html +3 -0
  93. package/docs/api/module-ai_nl-query-NLQuery.html +5 -0
  94. package/docs/api/module-ai_nl-query.NLQuery.html +3 -0
  95. package/docs/api/module-ai_nl-query.html +3 -0
  96. package/docs/api/module-ai_relationships-RelationshipDiscovery.html +6 -0
  97. package/docs/api/module-ai_relationships.RelationshipDiscovery.html +3 -0
  98. package/docs/api/module-ai_relationships.html +3 -0
  99. package/docs/api/module-ai_schema-extractor-SchemaExtractor.html +5 -0
  100. package/docs/api/module-ai_schema-extractor.SchemaExtractor.html +3 -0
  101. package/docs/api/module-ai_schema-extractor.html +3 -0
  102. package/docs/api/module-ai_spatial-SpatialAnalysis.html +6 -0
  103. package/docs/api/module-ai_spatial.SpatialAnalysis.html +3 -0
  104. package/docs/api/module-ai_spatial.html +3 -0
  105. package/docs/api/module-ai_tts-TTS.html +5 -0
  106. package/docs/api/module-ai_tts.TTS.html +3 -0
  107. package/docs/api/module-ai_tts.html +3 -0
  108. package/docs/api/module-content_social-protocols.html +3 -0
  109. package/docs/api/module-core_holosphere-HoloSphere.html +6 -0
  110. package/docs/api/module-core_holosphere.HoloSphere.html +3 -0
  111. package/docs/api/module-core_holosphere.html +3 -0
  112. package/docs/api/module-crypto_nostr-utils.html +3 -0
  113. package/docs/api/module-crypto_secp256k1.html +3 -0
  114. package/docs/api/module-federation_hologram.html +3 -0
  115. package/docs/api/module-hierarchical_upcast.html +3 -0
  116. package/docs/api/module-holosphere-HoloSphereBase.html +3 -0
  117. package/docs/api/module-holosphere.html +3 -0
  118. package/docs/api/module-lib_ai-methods.html +3 -0
  119. package/docs/api/module-lib_contract-methods.html +3 -0
  120. package/docs/api/module-lib_errors-AuthorizationError.html +3 -0
  121. package/docs/api/module-lib_errors-ValidationError.html +3 -0
  122. package/docs/api/module-lib_errors.AuthorizationError.html +3 -0
  123. package/docs/api/module-lib_errors.ValidationError.html +3 -0
  124. package/docs/api/module-lib_errors.html +3 -0
  125. package/docs/api/module-lib_federation-methods.html +3 -0
  126. package/docs/api/module-lib_index.html +3 -0
  127. package/docs/api/module-schema_validator-ValidationError.html +3 -0
  128. package/docs/api/module-schema_validator.ValidationError.html +3 -0
  129. package/docs/api/module-schema_validator.html +3 -0
  130. package/docs/api/module-spatial_h3-operations.html +4 -0
  131. package/docs/api/module-storage_backend-factory.BackendFactory.html +3 -0
  132. package/docs/api/module-storage_backend-factory.html +3 -0
  133. package/docs/api/module-storage_backend-interface-StorageBackend.html +3 -0
  134. package/docs/api/module-storage_backend-interface.StorageBackend.html +3 -0
  135. package/docs/api/module-storage_backend-interface.html +3 -0
  136. package/docs/api/module-storage_backends_activitypub-backend-ActivityPubBackend.html +7 -0
  137. package/docs/api/module-storage_backends_activitypub-backend.ActivityPubBackend.html +3 -0
  138. package/docs/api/module-storage_backends_activitypub-backend.html +3 -0
  139. package/docs/api/module-storage_backends_activitypub_server-ActivityPubServer.html +8 -0
  140. package/docs/api/module-storage_backends_activitypub_server.ActivityPubServer.html +3 -0
  141. package/docs/api/module-storage_backends_activitypub_server.html +3 -0
  142. package/docs/api/module-storage_backends_gundb-backend-GunDBBackend.html +7 -0
  143. package/docs/api/module-storage_backends_gundb-backend.GunDBBackend.html +3 -0
  144. package/docs/api/module-storage_backends_gundb-backend.html +3 -0
  145. package/docs/api/module-storage_backends_nostr-backend-NostrBackend.html +8 -0
  146. package/docs/api/module-storage_backends_nostr-backend.NostrBackend.html +3 -0
  147. package/docs/api/module-storage_backends_nostr-backend.html +3 -0
  148. package/docs/api/module-storage_filesystem-storage-FileSystemStorage.html +5 -0
  149. package/docs/api/module-storage_filesystem-storage-browser-FileSystemStorage.html +3 -0
  150. package/docs/api/module-storage_filesystem-storage-browser.FileSystemStorage.html +3 -0
  151. package/docs/api/module-storage_filesystem-storage-browser.html +3 -0
  152. package/docs/api/module-storage_filesystem-storage.FileSystemStorage.html +3 -0
  153. package/docs/api/module-storage_filesystem-storage.html +3 -0
  154. package/docs/api/module-storage_global-tables.html +3 -0
  155. package/docs/api/module-storage_gun-async.html +3 -0
  156. package/docs/api/module-storage_gun-auth-GunAuth.html +5 -0
  157. package/docs/api/module-storage_gun-auth.GunAuth.html +3 -0
  158. package/docs/api/module-storage_gun-auth.html +3 -0
  159. package/docs/api/module-storage_gun-federation.html +3 -0
  160. package/docs/api/module-storage_gun-references-GunReferenceHandler.html +5 -0
  161. package/docs/api/module-storage_gun-references.GunReferenceHandler.html +3 -0
  162. package/docs/api/module-storage_gun-references.html +3 -0
  163. package/docs/api/module-storage_gun-schema-GunSchemaValidator.html +5 -0
  164. package/docs/api/module-storage_gun-schema.GunSchemaValidator.html +3 -0
  165. package/docs/api/module-storage_gun-schema.html +3 -0
  166. package/docs/api/module-storage_gun-wrapper.html +11 -0
  167. package/docs/api/module-storage_indexeddb-storage-IndexedDBStorage.html +5 -0
  168. package/docs/api/module-storage_indexeddb-storage.IndexedDBStorage.html +3 -0
  169. package/docs/api/module-storage_indexeddb-storage.html +3 -0
  170. package/docs/api/module-storage_key-storage-simple.html +3 -0
  171. package/docs/api/module-storage_key-storage.html +4 -0
  172. package/docs/api/module-storage_memory-storage-MemoryStorage.html +5 -0
  173. package/docs/api/module-storage_memory-storage.MemoryStorage.html +3 -0
  174. package/docs/api/module-storage_memory-storage.html +3 -0
  175. package/docs/api/module-storage_migration-MigrationTool.html +6 -0
  176. package/docs/api/module-storage_migration.MigrationTool.html +3 -0
  177. package/docs/api/module-storage_migration.html +3 -0
  178. package/docs/api/module-storage_nostr-async.html +18 -0
  179. package/docs/api/module-storage_nostr-client-LRUCache.html +3 -0
  180. package/docs/api/module-storage_nostr-client-NostrClient.html +7 -0
  181. package/docs/api/module-storage_nostr-client.NostrClient.html +15 -0
  182. package/docs/api/module-storage_nostr-client.html +6 -0
  183. package/docs/api/module-storage_nostr-wrapper.html +3 -0
  184. package/docs/api/module-storage_outbox-queue-OutboxQueue.html +4 -0
  185. package/docs/api/module-storage_outbox-queue.OutboxQueue.html +3 -0
  186. package/docs/api/module-storage_outbox-queue.html +3 -0
  187. package/docs/api/module-storage_persistent-storage-PersistentStorage.html +3 -0
  188. package/docs/api/module-storage_persistent-storage.html +4 -0
  189. package/docs/api/module-storage_sync-service-SyncService.html +5 -0
  190. package/docs/api/module-storage_sync-service.SyncService.html +3 -0
  191. package/docs/api/module-storage_sync-service.html +3 -0
  192. package/docs/api/module-storage_unified-storage.html +3 -0
  193. package/docs/api/module-subscriptions_manager.SubscriptionRegistry.html +3 -0
  194. package/docs/api/module-subscriptions_manager.html +3 -0
  195. package/docs/api/schema_validator.js.html +113 -0
  196. package/docs/api/scripts/core.js +726 -0
  197. package/docs/api/scripts/core.min.js +23 -0
  198. package/docs/api/scripts/resize.js +90 -0
  199. package/docs/api/scripts/search.js +265 -0
  200. package/docs/api/scripts/search.min.js +6 -0
  201. package/docs/api/scripts/third-party/Apache-License-2.0.txt +202 -0
  202. package/docs/api/scripts/third-party/fuse.js +9 -0
  203. package/docs/api/scripts/third-party/hljs-line-num-original.js +369 -0
  204. package/docs/api/scripts/third-party/hljs-line-num.js +1 -0
  205. package/docs/api/scripts/third-party/hljs-original.js +5171 -0
  206. package/docs/api/scripts/third-party/hljs.js +1 -0
  207. package/docs/api/scripts/third-party/popper.js +5 -0
  208. package/docs/api/scripts/third-party/tippy.js +1 -0
  209. package/docs/api/scripts/third-party/tocbot.js +672 -0
  210. package/docs/api/scripts/third-party/tocbot.min.js +1 -0
  211. package/docs/api/spatial_h3-operations.js.html +129 -0
  212. package/docs/api/storage_backend-factory.js.html +133 -0
  213. package/docs/api/storage_backend-interface.js.html +164 -0
  214. package/docs/api/storage_backends_activitypub-backend.js.html +298 -0
  215. package/docs/api/storage_backends_activitypub_server.js.html +678 -0
  216. package/docs/api/storage_backends_gundb-backend.js.html +878 -0
  217. package/docs/api/storage_backends_nostr-backend.js.html +254 -0
  218. package/docs/api/storage_filesystem-storage-browser.js.html +83 -0
  219. package/docs/api/storage_filesystem-storage.js.html +207 -0
  220. package/docs/api/storage_global-tables.js.html +116 -0
  221. package/docs/api/storage_gun-async.js.html +344 -0
  222. package/docs/api/storage_gun-auth.js.html +376 -0
  223. package/docs/api/storage_gun-federation.js.html +788 -0
  224. package/docs/api/storage_gun-references.js.html +212 -0
  225. package/docs/api/storage_gun-schema.js.html +309 -0
  226. package/docs/api/storage_gun-wrapper.js.html +645 -0
  227. package/docs/api/storage_indexeddb-storage.js.html +224 -0
  228. package/docs/api/storage_key-storage-simple.js.html +102 -0
  229. package/docs/api/storage_key-storage.js.html +171 -0
  230. package/docs/api/storage_memory-storage.js.html +128 -0
  231. package/docs/api/storage_migration.js.html +354 -0
  232. package/docs/api/storage_nostr-async.js.html +1076 -0
  233. package/docs/api/storage_nostr-client.js.html +1598 -0
  234. package/docs/api/storage_nostr-wrapper.js.html +218 -0
  235. package/docs/api/storage_outbox-queue.js.html +248 -0
  236. package/docs/api/storage_persistent-storage.js.html +160 -0
  237. package/docs/api/storage_sync-service.js.html +201 -0
  238. package/docs/api/storage_unified-storage.js.html +157 -0
  239. package/docs/api/styles/clean-jsdoc-theme-base.css +1159 -0
  240. package/docs/api/styles/clean-jsdoc-theme-dark.css +412 -0
  241. package/docs/api/styles/clean-jsdoc-theme-light.css +482 -0
  242. package/docs/api/styles/clean-jsdoc-theme-scrollbar.css +30 -0
  243. package/docs/api/styles/clean-jsdoc-theme-without-scrollbar.min.css +1 -0
  244. package/docs/api/styles/clean-jsdoc-theme.min.css +1 -0
  245. package/docs/api/subscriptions_manager.js.html +162 -0
  246. package/examples/holosphere-widget.js +1242 -0
  247. package/examples/widget-demo.html +274 -0
  248. package/examples/widget.html +703 -0
  249. package/jsdoc.json +26 -0
  250. package/package.json +16 -3
  251. package/src/ai/aggregation.js +13 -2
  252. package/src/ai/breakdown.js +12 -2
  253. package/src/ai/classifier.js +14 -3
  254. package/src/ai/council.js +22 -7
  255. package/src/ai/embeddings.js +37 -15
  256. package/src/ai/federation-ai.js +13 -2
  257. package/src/ai/h3-ai.js +14 -2
  258. package/src/ai/index.js +16 -7
  259. package/src/ai/json-ops.js +18 -5
  260. package/src/ai/llm-service.js +62 -31
  261. package/src/ai/nl-query.js +12 -2
  262. package/src/ai/relationships.js +13 -2
  263. package/src/ai/schema-extractor.js +24 -10
  264. package/src/ai/spatial.js +13 -2
  265. package/src/ai/tts.js +25 -8
  266. package/src/cdn-entry.js +22 -0
  267. package/src/content/social-protocols.js +34 -25
  268. package/src/contracts/chain-manager.js +68 -40
  269. package/src/contracts/deployer.js +70 -42
  270. package/src/contracts/event-listener.js +61 -29
  271. package/src/contracts/holon-contracts.js +46 -31
  272. package/src/contracts/index.js +5 -6
  273. package/src/contracts/networks.js +19 -14
  274. package/src/contracts/operations.js +58 -41
  275. package/src/contracts/queries.js +70 -21
  276. package/src/core/holosphere.js +37 -8
  277. package/src/crypto/nostr-utils.js +105 -65
  278. package/src/crypto/secp256k1.js +7 -2
  279. package/src/federation/handshake.js +23 -11
  280. package/src/federation/hologram.js +9 -1
  281. package/src/hierarchical/upcast.js +34 -20
  282. package/src/index.js +671 -7
  283. package/src/lib/ai-methods.js +352 -3
  284. package/src/lib/contract-methods.js +152 -3
  285. package/src/lib/errors.js +31 -1
  286. package/src/lib/federation-methods.js +110 -3
  287. package/src/lib/index.js +9 -5
  288. package/src/schema/validator.js +22 -3
  289. package/src/spatial/h3-operations.js +17 -1
  290. package/src/storage/backend-factory.js +7 -2
  291. package/src/storage/backend-interface.js +21 -2
  292. package/src/storage/backends/activitypub/server.js +25 -3
  293. package/src/storage/backends/activitypub-backend.js +25 -2
  294. package/src/storage/backends/gundb-backend.js +322 -11
  295. package/src/storage/backends/nostr-backend.js +116 -1
  296. package/src/storage/filesystem-storage-browser.js +42 -2
  297. package/src/storage/filesystem-storage.js +72 -5
  298. package/src/storage/global-tables.js +7 -2
  299. package/src/storage/gun-async.js +20 -11
  300. package/src/storage/gun-auth.js +15 -4
  301. package/src/storage/gun-federation.js +14 -5
  302. package/src/storage/gun-references.js +16 -5
  303. package/src/storage/gun-schema.js +25 -10
  304. package/src/storage/gun-wrapper.js +160 -49
  305. package/src/storage/indexeddb-storage.js +65 -4
  306. package/src/storage/key-storage-simple.js +32 -9
  307. package/src/storage/key-storage.js +45 -13
  308. package/src/storage/memory-storage.js +65 -4
  309. package/src/storage/migration.js +20 -7
  310. package/src/storage/nostr-async.js +195 -90
  311. package/src/storage/nostr-client.js +173 -49
  312. package/src/storage/nostr-wrapper.js +6 -2
  313. package/src/storage/outbox-queue.js +55 -18
  314. package/src/storage/persistent-storage.js +56 -13
  315. package/src/storage/sync-service.js +51 -17
  316. package/src/storage/unified-storage.js +38 -3
  317. package/src/subscriptions/manager.js +33 -16
  318. package/vite.config.cdn.js +60 -0
  319. package/dist/index-Bvwyvd0T.cjs +0 -5
  320. package/dist/index-Bvwyvd0T.cjs.map +0 -1
  321. package/dist/index-d6f4RJBM.js.map +0 -1
  322. package/dist/indexeddb-storage-D8kOl0oK.js.map +0 -1
  323. package/dist/indexeddb-storage-a8GipaDr.cjs.map +0 -1
  324. package/dist/memory-storage-DBQK622V.js.map +0 -1
  325. package/dist/memory-storage-gfRovk2O.cjs.map +0 -1
  326. /package/{cleanup-test-data.js → scripts/cleanup-test-data.js} +0 -0
  327. /package/{test-ai-real-api.js → scripts/test-ai-real-api.js} +0 -0
@@ -1,7 +1,11 @@
1
1
  /**
2
- * GunDB Storage Wrapper with radisk persistence
3
- * Handles path construction and CRUD operations
4
- * Note: GunDB doesn't handle nested objects well, so we store data as JSON strings
2
+ * @fileoverview GunDB Storage Wrapper with radisk persistence.
3
+ *
4
+ * Handles path construction and CRUD operations for GunDB storage backend.
5
+ * Note: GunDB doesn't handle nested objects well, so we store data as JSON strings.
6
+ * Provides both holon-specific paths and global table operations.
7
+ *
8
+ * @module storage/gun-wrapper
5
9
  */
6
10
 
7
11
  import { gunPromise, gunPut, gunCollect } from './gun-async.js';
@@ -11,12 +15,16 @@ import { gunPromise, gunPut, gunCollect } from './gun-async.js';
11
15
  // ============================================================================
12
16
 
13
17
  /**
14
- * Build Gun path from components
18
+ * Build Gun path from components.
19
+ *
15
20
  * @param {string} appname - Application namespace
16
21
  * @param {string} holon - Holon ID (H3 or URI)
17
22
  * @param {string} lens - Lens name
18
- * @param {string} key - Data key (optional)
23
+ * @param {string} [key=null] - Data key (optional)
19
24
  * @returns {string} Gun path
25
+ * @example
26
+ * const path = buildPath('myapp', 'holon123', 'items', 'item1');
27
+ * // Returns: 'myapp/holon123/items/item1'
20
28
  */
21
29
  export function buildPath(appname, holon, lens, key = null) {
22
30
  // Encode components to handle special characters
@@ -31,11 +39,15 @@ export function buildPath(appname, holon, lens, key = null) {
31
39
  }
32
40
 
33
41
  /**
34
- * Build Gun path for global tables (app-wide data not tied to holons)
42
+ * Build Gun path for global tables (app-wide data not tied to holons).
43
+ *
35
44
  * @param {string} appname - Application namespace
36
45
  * @param {string} tableName - Global table name (e.g., 'schemas', 'federation')
37
- * @param {string} key - Data key (optional)
46
+ * @param {string} [key=null] - Data key (optional)
38
47
  * @returns {string} Gun path
48
+ * @example
49
+ * const path = buildGlobalPath('myapp', 'schemas', 'user');
50
+ * // Returns: 'myapp/schemas/user'
39
51
  */
40
52
  export function buildGlobalPath(appname, tableName, key = null) {
41
53
  const encodedTable = encodePathComponent(tableName);
@@ -47,33 +59,63 @@ export function buildGlobalPath(appname, tableName, key = null) {
47
59
  }
48
60
 
49
61
  /**
50
- * Encode path component to handle special characters
62
+ * Encode path component to handle special characters.
63
+ *
51
64
  * @private
65
+ * @param {string} component - Component to encode
66
+ * @returns {string} Encoded component
52
67
  */
53
68
  function encodePathComponent(component) {
54
69
  return encodeURIComponent(component).replace(/%2F/g, '/');
55
70
  }
56
71
 
57
72
  /**
58
- * Serialize data for GunDB storage
59
- * Wraps data in { _json: string } format for Gun compatibility
60
- * Gun requires an object at graph roots, so we can't store raw JSON strings
61
- * The deserialization handles both this format and raw JSON strings for reading old data
73
+ * Navigate to a Gun path using chained .get() calls.
74
+ *
75
+ * Gun treats 'a/b/c' as a literal key, not a path.
76
+ * This function splits the path and chains .get() calls properly.
77
+ *
62
78
  * @private
79
+ * @param {Object} gun - Gun instance
80
+ * @param {string} path - Path string like "appname/holon/lens/key"
81
+ * @returns {Object} Gun chain reference at the path
82
+ */
83
+ function getGunPath(gun, path) {
84
+ const parts = path.split('/').filter(p => p.length > 0);
85
+ let ref = gun;
86
+ for (const part of parts) {
87
+ ref = ref.get(part);
88
+ }
89
+ return ref;
90
+ }
91
+
92
+ /**
93
+ * Serialize data for GunDB storage.
94
+ *
95
+ * Stores data as raw JSON string for compatibility with holosphere original.
96
+ * This matches the format used in holosphere v1 for better interoperability.
97
+ *
98
+ * @private
99
+ * @param {Object} data - Data to serialize
100
+ * @returns {string} JSON string
63
101
  */
64
102
  function serializeForGun(data) {
65
- return { _json: JSON.stringify(data) };
103
+ return JSON.stringify(data);
66
104
  }
67
105
 
68
106
  /**
69
- * Deserialize data from GunDB storage
107
+ * Deserialize data from GunDB storage.
108
+ *
70
109
  * Handles multiple formats:
71
110
  * - Direct JSON string (holosphere original - now default)
72
111
  * - _json wrapped format (holosphere2 legacy)
73
112
  * - Gun internal references (_["#"])
74
113
  * - Gun node data (_[">"])
75
114
  * - Plain objects
115
+ *
76
116
  * @private
117
+ * @param {*} data - Raw data from Gun
118
+ * @returns {Object|null} Parsed data or null
77
119
  */
78
120
  function deserializeFromGun(data) {
79
121
  if (!data) {
@@ -143,32 +185,55 @@ function deserializeFromGun(data) {
143
185
  }
144
186
 
145
187
  /**
146
- * Write data to Gun with radisk persistence
188
+ * Write data to Gun with radisk persistence.
189
+ *
147
190
  * @param {Object} gun - Gun instance
148
191
  * @param {string} path - Gun path
149
192
  * @param {Object} data - Data to write
150
- * @returns {Promise<boolean>} Success indicator
193
+ * @returns {Promise<Object>} Success object with ok and timeout flags
194
+ * @example
195
+ * const result = await write(gun, 'myapp/holon1/items/item1', { name: 'Test' });
196
+ * if (result.timeout) console.warn('Write may not be persisted');
151
197
  */
152
198
  export async function write(gun, path, data) {
153
199
  try {
154
200
  const serialized = serializeForGun(data);
155
- await gunPut(gun.get(path), serialized, 2000);
156
- // Delay to allow Gun to propagate the write (50ms for better reliability)
157
- await new Promise(resolve => setTimeout(resolve, 50));
158
- return true;
201
+ const parts = path.split('/').filter(p => p.length > 0);
202
+ console.log('[gun-wrapper] write:', { path, parts, dataId: data?.id });
203
+ const ref = getGunPath(gun, path);
204
+ console.log('[gun-wrapper] write ref soul:', ref?._.get);
205
+ const ack = await gunPut(ref, serialized, 5000); // Increased timeout from 2s to 5s
206
+ console.log('[gun-wrapper] write ack:', { ok: ack.ok, timeout: ack.timeout });
207
+ if (ack.timeout) {
208
+ console.warn('[gun-wrapper] write timed out (data may not be persisted):', path);
209
+ }
210
+ console.log('[gun-wrapper] write complete:', path);
211
+
212
+ // Return ack info so caller can handle timeouts
213
+ return { ok: true, timeout: ack.timeout || false };
159
214
  } catch (error) {
215
+ console.error('[gun-wrapper] write error:', error);
160
216
  throw error;
161
217
  }
162
218
  }
163
219
 
164
220
  /**
165
- * Read data from Gun
221
+ * Read data from Gun.
222
+ *
166
223
  * @param {Object} gun - Gun instance
167
224
  * @param {string} path - Gun path
168
- * @returns {Promise<Object|null>} Data or null if not found
225
+ * @returns {Promise<Object|null>} Data or null if not found or deleted
226
+ * @example
227
+ * const data = await read(gun, 'myapp/holon1/items/item1');
228
+ * if (data) console.log(data.name);
169
229
  */
170
230
  export async function read(gun, path) {
171
- const rawData = await gunPromise(gun.get(path), 2000);
231
+ const parts = path.split('/').filter(p => p.length > 0);
232
+ console.log('[gun-wrapper] read:', { path, parts });
233
+ const ref = getGunPath(gun, path);
234
+ console.log('[gun-wrapper] read ref soul:', ref?._.get);
235
+ const rawData = await gunPromise(ref, 2000);
236
+ console.log('[gun-wrapper] read rawData:', rawData ? (typeof rawData === 'string' ? rawData.substring(0, 100) : 'object') : 'null');
172
237
 
173
238
  if (!rawData) {
174
239
  return null;
@@ -185,26 +250,36 @@ export async function read(gun, path) {
185
250
  }
186
251
 
187
252
  /**
188
- * Read all data under a path (lens query)
189
- * First gets the count of expected items, then collects until count is reached
253
+ * Read all data under a path (lens query).
254
+ *
255
+ * First gets the count of expected items, then collects until count is reached.
256
+ *
190
257
  * @param {Object} gun - Gun instance
191
258
  * @param {string} path - Gun path
192
- * @param {number} timeout - Maximum timeout in ms (default 5000)
259
+ * @param {number} [timeout=5000] - Maximum timeout in ms
193
260
  * @returns {Promise<Object[]>} Array of data objects
261
+ * @example
262
+ * const items = await readAll(gun, 'myapp/holon1/items');
263
+ * console.log(`Found ${items.length} items`);
194
264
  */
195
265
  export async function readAll(gun, path, timeout = 5000) {
266
+ const parts = path.split('/').filter(p => p.length > 0);
267
+ console.log('[gun-wrapper] readAll:', { path, parts });
268
+
196
269
  return new Promise((resolve) => {
197
270
  const output = new Map();
198
271
  let settled = false;
199
272
  let expectedCount = 0;
200
273
  let receivedCount = 0;
201
274
 
202
- const ref = gun.get(path);
275
+ const ref = getGunPath(gun, path);
276
+ console.log('[gun-wrapper] readAll ref soul:', ref?._.get);
203
277
 
204
278
  const tryResolve = () => {
205
279
  if (settled) return;
206
280
  if (expectedCount > 0 && receivedCount >= expectedCount) {
207
281
  settled = true;
282
+ console.log('[gun-wrapper] readAll resolved with', output.size, 'items');
208
283
  resolve(Array.from(output.values()));
209
284
  }
210
285
  };
@@ -216,18 +291,24 @@ export async function readAll(gun, path, timeout = 5000) {
216
291
  // Step 1: Get the parent data to count expected items
217
292
  ref.once((parentData) => {
218
293
  if (settled) return;
294
+ console.log('[gun-wrapper] readAll parentData:', parentData);
295
+ console.log('[gun-wrapper] readAll parentData keys:', parentData ? Object.keys(parentData).filter(k => k !== '_') : 'null');
296
+ console.log('[gun-wrapper] readAll parentData type:', typeof parentData);
219
297
 
220
298
  if (!parentData) {
221
299
  settled = true;
300
+ console.log('[gun-wrapper] readAll: no parent data, returning empty');
222
301
  resolve([]);
223
302
  return;
224
303
  }
225
304
 
226
305
  // Get all keys except Gun metadata
227
306
  const keys = Object.keys(parentData).filter(k => k !== '_');
307
+ console.log('[gun-wrapper] readAll keys:', keys);
228
308
 
229
309
  if (keys.length === 0) {
230
310
  settled = true;
311
+ console.log('[gun-wrapper] readAll: no keys, returning empty');
231
312
  resolve([]);
232
313
  return;
233
314
  }
@@ -291,14 +372,17 @@ export async function readAll(gun, path, timeout = 5000) {
291
372
  }
292
373
 
293
374
  /**
294
- * Update data (merge fields)
375
+ * Update data (merge fields).
376
+ *
295
377
  * @param {Object} gun - Gun instance
296
378
  * @param {string} path - Gun path
297
379
  * @param {Object} updates - Fields to update
298
380
  * @returns {Promise<boolean>} Success indicator
381
+ * @example
382
+ * const success = await update(gun, 'myapp/holon1/items/item1', { status: 'active' });
299
383
  */
300
384
  export async function update(gun, path, updates) {
301
- const rawData = await gunPromise(gun.get(path));
385
+ const rawData = await gunPromise(getGunPath(gun, path));
302
386
 
303
387
  if (!rawData) {
304
388
  return false; // Not found
@@ -315,7 +399,9 @@ export async function update(gun, path, updates) {
315
399
 
316
400
  try {
317
401
  const serialized = serializeForGun(merged);
318
- await gunPut(gun.get(path), serialized);
402
+ await gunPut(getGunPath(gun, path), serialized, 2000);
403
+ // Add delay for propagation
404
+ await new Promise(resolve => setTimeout(resolve, 200));
319
405
  return true;
320
406
  } catch (error) {
321
407
  throw error;
@@ -323,15 +409,18 @@ export async function update(gun, path, updates) {
323
409
  }
324
410
 
325
411
  /**
326
- * Delete data (tombstone)
412
+ * Delete data (tombstone).
413
+ *
327
414
  * @param {Object} gun - Gun instance
328
415
  * @param {string} path - Gun path
329
416
  * @returns {Promise<boolean>} Success indicator
417
+ * @example
418
+ * const deleted = await deleteData(gun, 'myapp/holon1/items/item1');
330
419
  */
331
420
  export async function deleteData(gun, path) {
332
421
  try {
333
422
  // First read existing data to get the id
334
- const rawData = await gunPromise(gun.get(path));
423
+ const rawData = await gunPromise(getGunPath(gun, path));
335
424
  if (!rawData) {
336
425
  return true; // Already deleted/doesn't exist
337
426
  }
@@ -345,7 +434,9 @@ export async function deleteData(gun, path) {
345
434
  _deletedAt: Date.now()
346
435
  };
347
436
 
348
- await gunPut(gun.get(path), serializeForGun(tombstone));
437
+ await gunPut(getGunPath(gun, path), serializeForGun(tombstone), 2000);
438
+ // Add delay for propagation
439
+ await new Promise(resolve => setTimeout(resolve, 200));
349
440
  return true;
350
441
  } catch (error) {
351
442
  throw error;
@@ -353,7 +444,8 @@ export async function deleteData(gun, path) {
353
444
  }
354
445
 
355
446
  /**
356
- * Delete all data under path prefix (tombstone)
447
+ * Delete all data under path prefix (tombstone).
448
+ *
357
449
  * @param {Object} gun - Gun instance
358
450
  * @param {string} path - Gun path prefix
359
451
  * @returns {Promise<Object>} Deletion results { success: boolean, count: number }
@@ -374,13 +466,19 @@ export async function deleteAll(gun, path) {
374
466
  }
375
467
 
376
468
  /**
377
- * Subscribe to data changes
469
+ * Subscribe to data changes.
470
+ *
378
471
  * @param {Object} gun - Gun instance
379
472
  * @param {string} path - Gun path
380
- * @param {Function} callback - Called on data changes
381
- * @param {Object} options - Subscription options
382
- * @param {boolean} options.prefix - Subscribe to all items under path (default: auto-detect)
473
+ * @param {Function} callback - Called on data changes (data, key) => void
474
+ * @param {Object} [options={}] - Subscription options
475
+ * @param {boolean} [options.prefix] - Subscribe to all items under path (default: auto-detect)
383
476
  * @returns {Object} Subscription object with unsubscribe method
477
+ * @example
478
+ * const sub = subscribe(gun, 'myapp/holon1/items', (data, key) => {
479
+ * console.log('Item changed:', key, data);
480
+ * });
481
+ * // Later: sub.unsubscribe();
384
482
  */
385
483
  export function subscribe(gun, path, callback, options = {}) {
386
484
  // Detect if this is a prefix subscription
@@ -389,7 +487,7 @@ export function subscribe(gun, path, callback, options = {}) {
389
487
 
390
488
  if (isPrefix) {
391
489
  // Subscribe to all items under this prefix
392
- const ref = gun.get(path);
490
+ const ref = getGunPath(gun, path);
393
491
 
394
492
  ref.map().on((data, key) => {
395
493
  if (data && !key.startsWith('_')) {
@@ -411,7 +509,7 @@ export function subscribe(gun, path, callback, options = {}) {
411
509
  };
412
510
  } else {
413
511
  // Subscribe to single item
414
- const listener = gun.get(path).on((data, key) => {
512
+ const listener = getGunPath(gun, path).on((data, key) => {
415
513
  if (data) {
416
514
  const deserialized = deserializeFromGun(data);
417
515
  if (deserialized && !deserialized._deleted) {
@@ -437,24 +535,31 @@ export function subscribe(gun, path, callback, options = {}) {
437
535
  // ============================================================================
438
536
 
439
537
  /**
440
- * Write data to a global table
441
- * Global tables are app-wide data not tied to specific holons (e.g., schemas, federation)
538
+ * Write data to a global table.
539
+ *
540
+ * Global tables are app-wide data not tied to specific holons (e.g., schemas, federation).
541
+ *
442
542
  * @param {Object} gun - Gun instance
443
543
  * @param {string} appname - Application namespace
444
544
  * @param {string} tableName - Global table name
445
545
  * @param {Object} data - Data to write (must have 'id' field)
446
- * @returns {Promise<boolean>} Success indicator
546
+ * @returns {Promise<Object>} Success object with ok and timeout flags
547
+ * @throws {Error} If data doesn't have an id field
548
+ * @example
549
+ * await writeGlobal(gun, 'myapp', 'schemas', { id: 'user', type: 'object' });
447
550
  */
448
551
  export async function writeGlobal(gun, appname, tableName, data) {
449
552
  if (!data || !data.id) {
450
553
  throw new Error('writeGlobal: data must have an id field');
451
554
  }
452
555
  const path = buildGlobalPath(appname, tableName, data.id);
556
+ // Use write function which includes the propagation delay
453
557
  return write(gun, path, data);
454
558
  }
455
559
 
456
560
  /**
457
- * Read data from a global table
561
+ * Read data from a global table.
562
+ *
458
563
  * @param {Object} gun - Gun instance
459
564
  * @param {string} appname - Application namespace
460
565
  * @param {string} tableName - Global table name
@@ -467,12 +572,14 @@ export async function readGlobal(gun, appname, tableName, key) {
467
572
  }
468
573
 
469
574
  /**
470
- * Read all data from a global table
471
- * Uses same approach as readAll
575
+ * Read all data from a global table.
576
+ *
577
+ * Uses same approach as readAll.
578
+ *
472
579
  * @param {Object} gun - Gun instance
473
580
  * @param {string} appname - Application namespace
474
581
  * @param {string} tableName - Global table name
475
- * @param {number} timeout - Timeout in ms (default: 2000)
582
+ * @param {number} [timeout=2000] - Timeout in ms
476
583
  * @returns {Promise<Object[]>} Array of data objects
477
584
  */
478
585
  export async function readAllGlobal(gun, appname, tableName, timeout = 2000) {
@@ -510,10 +617,13 @@ export async function deleteAllGlobal(gun, appname, tableName) {
510
617
  // ============================================================================
511
618
 
512
619
  /**
513
- * Parse data from Gun storage, handling various formats
620
+ * Parse data from Gun storage, handling various formats.
621
+ *
622
+ * Handles:
514
623
  * - JSON string in _json field
515
624
  * - Legacy object format
516
625
  * - Gun references
626
+ *
517
627
  * @param {*} rawData - Raw data from Gun
518
628
  * @returns {Object|null} Parsed data or null
519
629
  */
@@ -522,7 +632,8 @@ export function parse(rawData) {
522
632
  }
523
633
 
524
634
  /**
525
- * Serialize data for Gun storage
635
+ * Serialize data for Gun storage.
636
+ *
526
637
  * @param {Object} data - Data to serialize
527
638
  * @returns {string} JSON string
528
639
  */
@@ -1,20 +1,48 @@
1
1
  /**
2
- * IndexedDB storage adapter for browsers
3
- * Provides persistent storage with good performance
2
+ * @fileoverview IndexedDB storage adapter for browsers.
3
+ *
4
+ * Provides persistent storage with good performance using browser IndexedDB.
5
+ * Uses B-tree indexes for efficient prefix queries.
6
+ *
7
+ * @module storage/indexeddb-storage
4
8
  */
5
9
 
6
10
  import { PersistentStorage } from './persistent-storage.js';
7
11
 
12
+ /**
13
+ * IndexedDB storage adapter for browsers.
14
+ *
15
+ * Provides high-performance persistent storage using IndexedDB with efficient prefix queries.
16
+ *
17
+ * @class IndexedDBStorage
18
+ * @extends PersistentStorage
19
+ * @example
20
+ * const storage = new IndexedDBStorage();
21
+ * await storage.init('myapp');
22
+ * await storage.put('key1', { id: 'event1', content: 'test' });
23
+ */
8
24
  export class IndexedDBStorage extends PersistentStorage {
25
+ /**
26
+ * Create a new IndexedDBStorage instance.
27
+ */
9
28
  constructor() {
10
29
  super();
11
30
  /** @type {IDBDatabase|null} */
12
31
  this.db = null;
13
32
  /** @type {string} */
14
33
  this.dbName = '';
34
+ /** @type {string} */
15
35
  this.storeName = 'events';
16
36
  }
17
37
 
38
+ /**
39
+ * Initialize storage with namespace.
40
+ *
41
+ * Creates or opens the IndexedDB database and object store.
42
+ *
43
+ * @param {string} namespace - Storage namespace
44
+ * @returns {Promise<void>}
45
+ */
18
46
  async init(namespace) {
19
47
  this.dbName = `holosphere_${namespace}`;
20
48
 
@@ -41,6 +69,13 @@ export class IndexedDBStorage extends PersistentStorage {
41
69
  });
42
70
  }
43
71
 
72
+ /**
73
+ * Store an event.
74
+ *
75
+ * @param {string} key - Storage key
76
+ * @param {Object} event - Event data
77
+ * @returns {Promise<void>}
78
+ */
44
79
  async put(key, event) {
45
80
  return new Promise((resolve, reject) => {
46
81
  if (!this.db) {
@@ -57,6 +92,12 @@ export class IndexedDBStorage extends PersistentStorage {
57
92
  });
58
93
  }
59
94
 
95
+ /**
96
+ * Retrieve an event.
97
+ *
98
+ * @param {string} key - Storage key
99
+ * @returns {Promise<Object|null>} Event data or null
100
+ */
60
101
  async get(key) {
61
102
  return new Promise((resolve, reject) => {
62
103
  if (!this.db) {
@@ -77,8 +118,12 @@ export class IndexedDBStorage extends PersistentStorage {
77
118
  }
78
119
 
79
120
  /**
80
- * @param {string} prefix
81
- * @returns {Promise<any[]>}
121
+ * Retrieve all events matching a prefix.
122
+ *
123
+ * Uses IDBKeyRange for efficient B-tree index queries.
124
+ *
125
+ * @param {string} prefix - Key prefix to match
126
+ * @returns {Promise<any[]>} Array of matching events
82
127
  */
83
128
  async getAll(prefix) {
84
129
  return new Promise((resolve, reject) => {
@@ -119,6 +164,12 @@ export class IndexedDBStorage extends PersistentStorage {
119
164
  });
120
165
  }
121
166
 
167
+ /**
168
+ * Delete an event.
169
+ *
170
+ * @param {string} key - Storage key
171
+ * @returns {Promise<void>}
172
+ */
122
173
  async delete(key) {
123
174
  return new Promise((resolve, reject) => {
124
175
  if (!this.db) {
@@ -135,6 +186,11 @@ export class IndexedDBStorage extends PersistentStorage {
135
186
  });
136
187
  }
137
188
 
189
+ /**
190
+ * Clear all stored events.
191
+ *
192
+ * @returns {Promise<void>}
193
+ */
138
194
  async clear() {
139
195
  return new Promise((resolve, reject) => {
140
196
  if (!this.db) {
@@ -151,6 +207,11 @@ export class IndexedDBStorage extends PersistentStorage {
151
207
  });
152
208
  }
153
209
 
210
+ /**
211
+ * Close the database connection.
212
+ *
213
+ * @returns {Promise<void>}
214
+ */
154
215
  async close() {
155
216
  if (this.db) {
156
217
  this.db.close();
@@ -1,9 +1,11 @@
1
1
  /**
2
- * Simple Key Storage for Node.js
3
- * Basic filesystem-based key management
2
+ * @fileoverview Simple Key Storage for Node.js.
4
3
  *
5
- * NOTE: This only works in Node.js, not browsers
6
- * For browsers, keys are managed in localStorage
4
+ * Basic filesystem-based key management.
5
+ * NOTE: This only works in Node.js, not browsers.
6
+ * For browsers, keys are managed in localStorage.
7
+ *
8
+ * @module storage/key-storage-simple
7
9
  */
8
10
 
9
11
  import fs from 'fs';
@@ -11,7 +13,10 @@ import path from 'path';
11
13
  import os from 'os';
12
14
 
13
15
  /**
14
- * Get key storage directory
16
+ * Get key storage directory.
17
+ *
18
+ * @private
19
+ * @returns {string} Key storage directory path
15
20
  */
16
21
  function getKeyDir() {
17
22
  const configDir = process.env.XDG_CONFIG_HOME ||
@@ -23,7 +28,11 @@ function getKeyDir() {
23
28
  }
24
29
 
25
30
  /**
26
- * Get key file path for an app
31
+ * Get key file path for an app.
32
+ *
33
+ * @private
34
+ * @param {string} appName - Application name
35
+ * @returns {string} Full path to key file
27
36
  */
28
37
  function getKeyPath(appName) {
29
38
  const safeName = appName.replace(/[^a-zA-Z0-9-_]/g, '_');
@@ -31,7 +40,10 @@ function getKeyPath(appName) {
31
40
  }
32
41
 
33
42
  /**
34
- * Load private key
43
+ * Load private key from filesystem.
44
+ *
45
+ * @param {string} appName - Application name
46
+ * @returns {string|null} Private key (hex) or null if not found
35
47
  */
36
48
  export function loadKey(appName) {
37
49
  const keyPath = getKeyPath(appName);
@@ -45,7 +57,12 @@ export function loadKey(appName) {
45
57
  }
46
58
 
47
59
  /**
48
- * Save private key
60
+ * Save private key to filesystem with secure permissions.
61
+ *
62
+ * @param {string} appName - Application name
63
+ * @param {string} privateKey - Private key (64-character hex string)
64
+ * @returns {string} Path where key was saved
65
+ * @throws {Error} If private key format is invalid
49
66
  */
50
67
  export function saveKey(appName, privateKey) {
51
68
  if (!/^[0-9a-f]{64}$/i.test(privateKey)) {
@@ -64,7 +81,13 @@ export function saveKey(appName, privateKey) {
64
81
  }
65
82
 
66
83
  /**
67
- * Get or create key
84
+ * Get or create private key.
85
+ *
86
+ * Loads existing key or generates and saves a new one.
87
+ *
88
+ * @param {string} appName - Application name
89
+ * @param {Function} generateFn - Function to generate new key (returns hex string)
90
+ * @returns {string} Private key (hex)
68
91
  */
69
92
  export function getOrCreateKey(appName, generateFn) {
70
93
  const existing = loadKey(appName);