@geoprotocol/geo-sdk 0.18.3 → 0.20.0-beta.0

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 (356) hide show
  1. package/README.md +1047 -371
  2. package/dist/contracts.d.ts +0 -11
  3. package/dist/contracts.d.ts.map +1 -1
  4. package/dist/contracts.js +0 -11
  5. package/dist/contracts.js.map +1 -1
  6. package/dist/index.d.ts +8 -1
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +8 -1
  9. package/dist/index.js.map +1 -1
  10. package/dist/lite.d.ts +2 -0
  11. package/dist/lite.d.ts.map +1 -1
  12. package/dist/lite.js +2 -0
  13. package/dist/lite.js.map +1 -1
  14. package/dist/src/abis/dao-space-factory.d.ts +43 -6
  15. package/dist/src/abis/dao-space-factory.d.ts.map +1 -1
  16. package/dist/src/abis/dao-space-factory.js +38 -27
  17. package/dist/src/abis/dao-space-factory.js.map +1 -1
  18. package/dist/src/abis/dao-space-v2.test.d.ts +2 -0
  19. package/dist/src/abis/dao-space-v2.test.d.ts.map +1 -0
  20. package/dist/src/abis/dao-space-v2.test.js +67 -0
  21. package/dist/src/abis/dao-space-v2.test.js.map +1 -0
  22. package/dist/src/abis/dao-space.d.ts +98 -7
  23. package/dist/src/abis/dao-space.d.ts.map +1 -1
  24. package/dist/src/abis/dao-space.js +61 -99
  25. package/dist/src/abis/dao-space.js.map +1 -1
  26. package/dist/src/abis/index.d.ts +2 -2
  27. package/dist/src/abis/index.d.ts.map +1 -1
  28. package/dist/src/abis/index.js +2 -2
  29. package/dist/src/abis/index.js.map +1 -1
  30. package/dist/src/api-surface.e2e.test.d.ts +2 -0
  31. package/dist/src/api-surface.e2e.test.d.ts.map +1 -0
  32. package/dist/src/api-surface.e2e.test.js +1007 -0
  33. package/dist/src/api-surface.e2e.test.js.map +1 -0
  34. package/dist/src/client/api.d.ts +86 -0
  35. package/dist/src/client/api.d.ts.map +1 -0
  36. package/dist/src/client/api.js +169 -0
  37. package/dist/src/client/api.js.map +1 -0
  38. package/dist/src/client/comments.d.ts +59 -0
  39. package/dist/src/client/comments.d.ts.map +1 -0
  40. package/dist/src/client/comments.js +96 -0
  41. package/dist/src/client/comments.js.map +1 -0
  42. package/dist/src/client/context.d.ts +20 -0
  43. package/dist/src/client/context.d.ts.map +1 -0
  44. package/dist/src/client/context.js +20 -0
  45. package/dist/src/client/context.js.map +1 -0
  46. package/dist/src/client/dao-spaces.d.ts +380 -0
  47. package/dist/src/client/dao-spaces.d.ts.map +1 -0
  48. package/dist/src/client/dao-spaces.js +585 -0
  49. package/dist/src/client/dao-spaces.js.map +1 -0
  50. package/dist/src/client/dao-spaces.test.d.ts +2 -0
  51. package/dist/src/client/dao-spaces.test.d.ts.map +1 -0
  52. package/dist/src/client/dao-spaces.test.js +461 -0
  53. package/dist/src/client/dao-spaces.test.js.map +1 -0
  54. package/dist/src/client/edits.d.ts +100 -0
  55. package/dist/src/client/edits.d.ts.map +1 -0
  56. package/dist/src/client/edits.js +131 -0
  57. package/dist/src/client/edits.js.map +1 -0
  58. package/dist/src/client/edits.test.d.ts +2 -0
  59. package/dist/src/client/edits.test.d.ts.map +1 -0
  60. package/dist/src/client/edits.test.js +98 -0
  61. package/dist/src/client/edits.test.js.map +1 -0
  62. package/dist/src/client/entities.d.ts +23 -0
  63. package/dist/src/client/entities.d.ts.map +1 -0
  64. package/dist/src/client/entities.js +88 -0
  65. package/dist/src/client/entities.js.map +1 -0
  66. package/dist/src/client/entity-votes.d.ts +141 -0
  67. package/dist/src/client/entity-votes.d.ts.map +1 -0
  68. package/dist/src/client/entity-votes.js +168 -0
  69. package/dist/src/client/entity-votes.js.map +1 -0
  70. package/dist/src/client/entity-votes.test.d.ts +2 -0
  71. package/dist/src/client/entity-votes.test.d.ts.map +1 -0
  72. package/dist/src/client/entity-votes.test.js +93 -0
  73. package/dist/src/client/entity-votes.test.js.map +1 -0
  74. package/dist/src/client/graph-workflows.test.d.ts +2 -0
  75. package/dist/src/client/graph-workflows.test.d.ts.map +1 -0
  76. package/dist/src/client/graph-workflows.test.js +83 -0
  77. package/dist/src/client/graph-workflows.test.js.map +1 -0
  78. package/dist/src/client/images-storage.test.d.ts +2 -0
  79. package/dist/src/client/images-storage.test.d.ts.map +1 -0
  80. package/dist/src/client/images-storage.test.js +52 -0
  81. package/dist/src/client/images-storage.test.js.map +1 -0
  82. package/dist/src/client/images.d.ts +35 -0
  83. package/dist/src/client/images.d.ts.map +1 -0
  84. package/dist/src/client/images.js +90 -0
  85. package/dist/src/client/images.js.map +1 -0
  86. package/dist/src/client/personal-spaces.d.ts +155 -0
  87. package/dist/src/client/personal-spaces.d.ts.map +1 -0
  88. package/dist/src/client/personal-spaces.js +198 -0
  89. package/dist/src/client/personal-spaces.js.map +1 -0
  90. package/dist/src/client/spaces.test.d.ts +2 -0
  91. package/dist/src/client/spaces.test.d.ts.map +1 -0
  92. package/dist/src/client/spaces.test.js +157 -0
  93. package/dist/src/client/spaces.test.js.map +1 -0
  94. package/dist/src/client/storage.d.ts +52 -0
  95. package/dist/src/client/storage.d.ts.map +1 -0
  96. package/dist/src/client/storage.js +53 -0
  97. package/dist/src/client/storage.js.map +1 -0
  98. package/dist/src/client.d.ts +328 -0
  99. package/dist/src/client.d.ts.map +1 -0
  100. package/dist/src/client.js +441 -0
  101. package/dist/src/client.js.map +1 -0
  102. package/dist/src/client.test.d.ts +2 -0
  103. package/dist/src/client.test.d.ts.map +1 -0
  104. package/dist/src/client.test.js +125 -0
  105. package/dist/src/client.test.js.map +1 -0
  106. package/dist/src/contracts-v2/local-geobrowser.e2e.test.d.ts +2 -0
  107. package/dist/src/contracts-v2/local-geobrowser.e2e.test.d.ts.map +1 -0
  108. package/dist/src/contracts-v2/local-geobrowser.e2e.test.js +239 -0
  109. package/dist/src/contracts-v2/local-geobrowser.e2e.test.js.map +1 -0
  110. package/dist/src/dao-space/constants.d.ts +16 -3
  111. package/dist/src/dao-space/constants.d.ts.map +1 -1
  112. package/dist/src/dao-space/constants.js +16 -5
  113. package/dist/src/dao-space/constants.js.map +1 -1
  114. package/dist/src/dao-space/create-space.d.ts +1 -31
  115. package/dist/src/dao-space/create-space.d.ts.map +1 -1
  116. package/dist/src/dao-space/create-space.js +5 -70
  117. package/dist/src/dao-space/create-space.js.map +1 -1
  118. package/dist/src/dao-space/execute-proposal.d.ts +1 -22
  119. package/dist/src/dao-space/execute-proposal.d.ts.map +1 -1
  120. package/dist/src/dao-space/execute-proposal.js +12 -59
  121. package/dist/src/dao-space/execute-proposal.js.map +1 -1
  122. package/dist/src/dao-space/index.d.ts +2 -1
  123. package/dist/src/dao-space/index.d.ts.map +1 -1
  124. package/dist/src/dao-space/index.js +1 -0
  125. package/dist/src/dao-space/index.js.map +1 -1
  126. package/dist/src/dao-space/propose-add-editor.d.ts +1 -20
  127. package/dist/src/dao-space/propose-add-editor.d.ts.map +1 -1
  128. package/dist/src/dao-space/propose-add-editor.js +12 -87
  129. package/dist/src/dao-space/propose-add-editor.js.map +1 -1
  130. package/dist/src/dao-space/propose-add-editor.test.js +16 -4
  131. package/dist/src/dao-space/propose-add-editor.test.js.map +1 -1
  132. package/dist/src/dao-space/propose-add-member.d.ts +1 -20
  133. package/dist/src/dao-space/propose-add-member.d.ts.map +1 -1
  134. package/dist/src/dao-space/propose-add-member.js +12 -87
  135. package/dist/src/dao-space/propose-add-member.js.map +1 -1
  136. package/dist/src/dao-space/propose-add-member.test.js +13 -0
  137. package/dist/src/dao-space/propose-add-member.test.js.map +1 -1
  138. package/dist/src/dao-space/propose-edit.d.ts +1 -30
  139. package/dist/src/dao-space/propose-edit.d.ts.map +1 -1
  140. package/dist/src/dao-space/propose-edit.js +12 -108
  141. package/dist/src/dao-space/propose-edit.js.map +1 -1
  142. package/dist/src/dao-space/propose-edit.test.js +53 -1
  143. package/dist/src/dao-space/propose-edit.test.js.map +1 -1
  144. package/dist/src/dao-space/propose-remove-editor.d.ts +1 -22
  145. package/dist/src/dao-space/propose-remove-editor.d.ts.map +1 -1
  146. package/dist/src/dao-space/propose-remove-editor.js +12 -94
  147. package/dist/src/dao-space/propose-remove-editor.js.map +1 -1
  148. package/dist/src/dao-space/propose-remove-editor.test.js +24 -11
  149. package/dist/src/dao-space/propose-remove-editor.test.js.map +1 -1
  150. package/dist/src/dao-space/propose-remove-member.d.ts +1 -22
  151. package/dist/src/dao-space/propose-remove-member.d.ts.map +1 -1
  152. package/dist/src/dao-space/propose-remove-member.js +12 -94
  153. package/dist/src/dao-space/propose-remove-member.js.map +1 -1
  154. package/dist/src/dao-space/propose-remove-member.test.js +19 -0
  155. package/dist/src/dao-space/propose-remove-member.test.js.map +1 -1
  156. package/dist/src/dao-space/propose-request-membership.d.ts.map +1 -1
  157. package/dist/src/dao-space/propose-request-membership.js +4 -4
  158. package/dist/src/dao-space/propose-request-membership.js.map +1 -1
  159. package/dist/src/dao-space/propose-update-voting-settings.d.ts +8 -0
  160. package/dist/src/dao-space/propose-update-voting-settings.d.ts.map +1 -0
  161. package/dist/src/dao-space/propose-update-voting-settings.js +19 -0
  162. package/dist/src/dao-space/propose-update-voting-settings.js.map +1 -0
  163. package/dist/src/dao-space/propose-update-voting-settings.test.d.ts +2 -0
  164. package/dist/src/dao-space/propose-update-voting-settings.test.d.ts.map +1 -0
  165. package/dist/src/dao-space/propose-update-voting-settings.test.js +118 -0
  166. package/dist/src/dao-space/propose-update-voting-settings.test.js.map +1 -0
  167. package/dist/src/dao-space/types.d.ts +72 -11
  168. package/dist/src/dao-space/types.d.ts.map +1 -1
  169. package/dist/src/dao-space/vote-proposal.d.ts +1 -24
  170. package/dist/src/dao-space/vote-proposal.d.ts.map +1 -1
  171. package/dist/src/dao-space/vote-proposal.js +12 -64
  172. package/dist/src/dao-space/vote-proposal.js.map +1 -1
  173. package/dist/src/dao-space/vote-proposal.test.js +54 -0
  174. package/dist/src/dao-space/vote-proposal.test.js.map +1 -1
  175. package/dist/src/e2e-test-environment.d.ts +26 -0
  176. package/dist/src/e2e-test-environment.d.ts.map +1 -0
  177. package/dist/src/e2e-test-environment.js +150 -0
  178. package/dist/src/e2e-test-environment.js.map +1 -0
  179. package/dist/src/encoding.d.ts +3 -0
  180. package/dist/src/encoding.d.ts.map +1 -1
  181. package/dist/src/encoding.js +3 -0
  182. package/dist/src/encoding.js.map +1 -1
  183. package/dist/src/encodings/get-create-dao-space-calldata.d.ts +34 -97
  184. package/dist/src/encodings/get-create-dao-space-calldata.d.ts.map +1 -1
  185. package/dist/src/encodings/get-create-dao-space-calldata.js +66 -109
  186. package/dist/src/encodings/get-create-dao-space-calldata.js.map +1 -1
  187. package/dist/src/encodings/get-create-dao-space-calldata.test.js +90 -23
  188. package/dist/src/encodings/get-create-dao-space-calldata.test.js.map +1 -1
  189. package/dist/src/encodings/get-create-personal-space-calldata.d.ts +2 -0
  190. package/dist/src/encodings/get-create-personal-space-calldata.d.ts.map +1 -1
  191. package/dist/src/encodings/get-create-personal-space-calldata.js +2 -0
  192. package/dist/src/encodings/get-create-personal-space-calldata.js.map +1 -1
  193. package/dist/src/full-flow-test.test.js +5 -2
  194. package/dist/src/full-flow-test.test.js.map +1 -1
  195. package/dist/src/graph/comment-utils.d.ts +4 -0
  196. package/dist/src/graph/comment-utils.d.ts.map +1 -1
  197. package/dist/src/graph/comment-utils.js +4 -0
  198. package/dist/src/graph/comment-utils.js.map +1 -1
  199. package/dist/src/graph/constants.d.ts +12 -3
  200. package/dist/src/graph/constants.d.ts.map +1 -1
  201. package/dist/src/graph/constants.js +11 -5
  202. package/dist/src/graph/constants.js.map +1 -1
  203. package/dist/src/graph/create-comment.d.ts +3 -22
  204. package/dist/src/graph/create-comment.d.ts.map +1 -1
  205. package/dist/src/graph/create-comment.js +6 -136
  206. package/dist/src/graph/create-comment.js.map +1 -1
  207. package/dist/src/graph/create-comment.test.js +19 -8
  208. package/dist/src/graph/create-comment.test.js.map +1 -1
  209. package/dist/src/graph/create-entity.d.ts +2 -0
  210. package/dist/src/graph/create-entity.d.ts.map +1 -1
  211. package/dist/src/graph/create-entity.js +2 -0
  212. package/dist/src/graph/create-entity.js.map +1 -1
  213. package/dist/src/graph/create-image.d.ts +2 -21
  214. package/dist/src/graph/create-image.d.ts.map +1 -1
  215. package/dist/src/graph/create-image.js +7 -69
  216. package/dist/src/graph/create-image.js.map +1 -1
  217. package/dist/src/graph/create-property.d.ts +2 -0
  218. package/dist/src/graph/create-property.d.ts.map +1 -1
  219. package/dist/src/graph/create-property.js +2 -0
  220. package/dist/src/graph/create-property.js.map +1 -1
  221. package/dist/src/graph/create-proposal-review.d.ts +1 -27
  222. package/dist/src/graph/create-proposal-review.d.ts.map +1 -1
  223. package/dist/src/graph/create-proposal-review.js +4 -61
  224. package/dist/src/graph/create-proposal-review.js.map +1 -1
  225. package/dist/src/graph/create-relation.d.ts +2 -0
  226. package/dist/src/graph/create-relation.d.ts.map +1 -1
  227. package/dist/src/graph/create-relation.js +2 -0
  228. package/dist/src/graph/create-relation.js.map +1 -1
  229. package/dist/src/graph/create-type.d.ts +2 -0
  230. package/dist/src/graph/create-type.d.ts.map +1 -1
  231. package/dist/src/graph/create-type.js +2 -0
  232. package/dist/src/graph/create-type.js.map +1 -1
  233. package/dist/src/graph/delete-entity.d.ts +2 -14
  234. package/dist/src/graph/delete-entity.d.ts.map +1 -1
  235. package/dist/src/graph/delete-entity.js +5 -78
  236. package/dist/src/graph/delete-entity.js.map +1 -1
  237. package/dist/src/graph/delete-entity.test.js +7 -0
  238. package/dist/src/graph/delete-entity.test.js.map +1 -1
  239. package/dist/src/graph/delete-relation.d.ts +2 -0
  240. package/dist/src/graph/delete-relation.d.ts.map +1 -1
  241. package/dist/src/graph/delete-relation.js +2 -0
  242. package/dist/src/graph/delete-relation.js.map +1 -1
  243. package/dist/src/graph/entity-vote.d.ts +3 -3
  244. package/dist/src/graph/entity-vote.d.ts.map +1 -1
  245. package/dist/src/graph/entity-vote.js +21 -46
  246. package/dist/src/graph/entity-vote.js.map +1 -1
  247. package/dist/src/graph/update-comment.d.ts +3 -0
  248. package/dist/src/graph/update-comment.d.ts.map +1 -1
  249. package/dist/src/graph/update-comment.js +3 -0
  250. package/dist/src/graph/update-comment.js.map +1 -1
  251. package/dist/src/graph/update-entity.d.ts +2 -0
  252. package/dist/src/graph/update-entity.d.ts.map +1 -1
  253. package/dist/src/graph/update-entity.js +2 -0
  254. package/dist/src/graph/update-entity.js.map +1 -1
  255. package/dist/src/graph/update-proposal-review.d.ts +2 -27
  256. package/dist/src/graph/update-proposal-review.d.ts.map +1 -1
  257. package/dist/src/graph/update-proposal-review.js +4 -50
  258. package/dist/src/graph/update-proposal-review.js.map +1 -1
  259. package/dist/src/graph/update-relation.d.ts +2 -0
  260. package/dist/src/graph/update-relation.d.ts.map +1 -1
  261. package/dist/src/graph/update-relation.js +2 -0
  262. package/dist/src/graph/update-relation.js.map +1 -1
  263. package/dist/src/ipfs-core.d.ts +42 -0
  264. package/dist/src/ipfs-core.d.ts.map +1 -0
  265. package/dist/src/ipfs-core.js +165 -0
  266. package/dist/src/ipfs-core.js.map +1 -0
  267. package/dist/src/ipfs-core.test.d.ts +2 -0
  268. package/dist/src/ipfs-core.test.d.ts.map +1 -0
  269. package/dist/src/ipfs-core.test.js +56 -0
  270. package/dist/src/ipfs-core.test.js.map +1 -0
  271. package/dist/src/ipfs.d.ts +7 -52
  272. package/dist/src/ipfs.d.ts.map +1 -1
  273. package/dist/src/ipfs.js +27 -170
  274. package/dist/src/ipfs.js.map +1 -1
  275. package/dist/src/legacy-api-surface.e2e.test.d.ts +2 -0
  276. package/dist/src/legacy-api-surface.e2e.test.d.ts.map +1 -0
  277. package/dist/src/legacy-api-surface.e2e.test.js +860 -0
  278. package/dist/src/legacy-api-surface.e2e.test.js.map +1 -0
  279. package/dist/src/networks.d.ts +46 -0
  280. package/dist/src/networks.d.ts.map +1 -0
  281. package/dist/src/networks.js +90 -0
  282. package/dist/src/networks.js.map +1 -0
  283. package/dist/src/ops/comments.d.ts +80 -0
  284. package/dist/src/ops/comments.d.ts.map +1 -0
  285. package/dist/src/ops/comments.js +142 -0
  286. package/dist/src/ops/comments.js.map +1 -0
  287. package/dist/src/ops/entities.d.ts +50 -0
  288. package/dist/src/ops/entities.d.ts.map +1 -0
  289. package/dist/src/ops/entities.js +51 -0
  290. package/dist/src/ops/entities.js.map +1 -0
  291. package/dist/src/ops/index.d.ts +7 -0
  292. package/dist/src/ops/index.d.ts.map +1 -0
  293. package/dist/src/ops/index.js +7 -0
  294. package/dist/src/ops/index.js.map +1 -0
  295. package/dist/src/ops/index.test.d.ts +2 -0
  296. package/dist/src/ops/index.test.d.ts.map +1 -0
  297. package/dist/src/ops/index.test.js +115 -0
  298. package/dist/src/ops/index.test.js.map +1 -0
  299. package/dist/src/ops/properties.d.ts +20 -0
  300. package/dist/src/ops/properties.d.ts.map +1 -0
  301. package/dist/src/ops/properties.js +20 -0
  302. package/dist/src/ops/properties.js.map +1 -0
  303. package/dist/src/ops/proposal-reviews.d.ts +43 -0
  304. package/dist/src/ops/proposal-reviews.d.ts.map +1 -0
  305. package/dist/src/ops/proposal-reviews.js +103 -0
  306. package/dist/src/ops/proposal-reviews.js.map +1 -0
  307. package/dist/src/ops/relations.d.ts +57 -0
  308. package/dist/src/ops/relations.d.ts.map +1 -0
  309. package/dist/src/ops/relations.js +59 -0
  310. package/dist/src/ops/relations.js.map +1 -0
  311. package/dist/src/ops/types.d.ts +20 -0
  312. package/dist/src/ops/types.d.ts.map +1 -0
  313. package/dist/src/ops/types.js +20 -0
  314. package/dist/src/ops/types.js.map +1 -0
  315. package/dist/src/personal-space/constants.d.ts +1 -0
  316. package/dist/src/personal-space/constants.d.ts.map +1 -1
  317. package/dist/src/personal-space/constants.js +1 -0
  318. package/dist/src/personal-space/constants.js.map +1 -1
  319. package/dist/src/personal-space/create-space.d.ts +3 -20
  320. package/dist/src/personal-space/create-space.d.ts.map +1 -1
  321. package/dist/src/personal-space/create-space.js +5 -21
  322. package/dist/src/personal-space/create-space.js.map +1 -1
  323. package/dist/src/personal-space/has-space.d.ts +9 -2
  324. package/dist/src/personal-space/has-space.d.ts.map +1 -1
  325. package/dist/src/personal-space/has-space.js +9 -13
  326. package/dist/src/personal-space/has-space.js.map +1 -1
  327. package/dist/src/personal-space/has-space.test.d.ts +2 -0
  328. package/dist/src/personal-space/has-space.test.d.ts.map +1 -0
  329. package/dist/src/personal-space/has-space.test.js +30 -0
  330. package/dist/src/personal-space/has-space.test.js.map +1 -0
  331. package/dist/src/personal-space/publish-edit.d.ts +1 -22
  332. package/dist/src/personal-space/publish-edit.d.ts.map +1 -1
  333. package/dist/src/personal-space/publish-edit.js +10 -64
  334. package/dist/src/personal-space/publish-edit.js.map +1 -1
  335. package/dist/src/personal-space/types.d.ts +4 -1
  336. package/dist/src/personal-space/types.d.ts.map +1 -1
  337. package/dist/src/smart-wallet.d.ts +2 -4
  338. package/dist/src/smart-wallet.d.ts.map +1 -1
  339. package/dist/src/smart-wallet.js +6 -9
  340. package/dist/src/smart-wallet.js.map +1 -1
  341. package/dist/src/types.d.ts +25 -0
  342. package/dist/src/types.d.ts.map +1 -1
  343. package/dist/src/types.js.map +1 -1
  344. package/package.json +16 -2
  345. package/dist/proto.d.ts +0 -5
  346. package/dist/proto.d.ts.map +0 -1
  347. package/dist/proto.js +0 -5
  348. package/dist/proto.js.map +0 -1
  349. package/dist/src/graph/create-space.d.ts +0 -37
  350. package/dist/src/graph/create-space.d.ts.map +0 -1
  351. package/dist/src/graph/create-space.js +0 -67
  352. package/dist/src/graph/create-space.js.map +0 -1
  353. package/dist/src/proto/index.d.ts +0 -7
  354. package/dist/src/proto/index.d.ts.map +0 -1
  355. package/dist/src/proto/index.js +0 -7
  356. package/dist/src/proto/index.js.map +0 -1
package/README.md CHANGED
@@ -14,623 +14,1299 @@ If you're using an AI coding assistant (e.g. Claude Code, Codex, Cursor) to work
14
14
 
15
15
  ## Overview
16
16
 
17
- ### Data flow
17
+ ### Data Flow
18
18
 
19
19
  Data in The Graph lives both offchain and onchain. This data is written to IPFS, and the resulting content identifier is then posted onchain before being read by the indexing stack. After the indexer finishes processing the data it's exposed by the API.
20
+
20
21
  ![CleanShot 2025-01-22 at 10 51 23@2x](https://github.com/user-attachments/assets/f0cee8e0-43f9-4663-a2e7-54de6d962115)
21
22
 
22
23
  ### Spaces
23
24
 
24
- On The Graph, knowledge is organized into spaces. Anyone can create a space for a community, project or individual. Spaces are organized onchain into a set of multiple smart contracts. These smart contracts represent the space itself, its data and its governance process. Depending on which onchain actions you're taking you might be interacting with one or more of these smart contracts.
25
-
26
- ### Relations
27
-
28
- Relations describe the edges within the graph. Relations are themselves entities that include details about the relationship. For example a Company can have Team Members. Each Team Member relation can have an attribute describing when the person joined the team. This is a model that is commonly called a property graph.
25
+ On The Graph, knowledge is organized into spaces. Anyone can create a space for a community, project or individual. Spaces are organized onchain into a set of smart contracts. These contracts represent the space itself, its data and its governance process.
29
26
 
30
27
  ### Entities
31
28
 
32
- An entity is a unique identifier representing a person, a place, an idea, a concept, or anything else. Entities are comprised of triples and relations which provide semantic meaning as to what the entity _is_. An entity's data can be composed from multiple spaces at once. This property is what enables pluralism within The Graph.
29
+ An entity is a unique identifier representing a person, a place, an idea, a concept, or anything else. Entities are composed from triples and relations that describe what the entity is. An entity's data can be composed from multiple spaces at once. This property is what enables pluralism within The Graph.
33
30
 
34
31
  [More about entities and knowledge graphs](https://www.geobrowser.io/space/720eb279c64d56735dccd17a2a416ba2/a72654b014fa405bbf1250ba901bc082)
35
32
 
36
- ### Ops and edits
33
+ ### Relations
34
+
35
+ Relations describe the edges within the graph. Relations are themselves entities that include details about the relationship. For example, a Company can have Team Members. Each Team Member relation can have an attribute describing when the person joined the team. This is a property graph model.
36
+
37
+ ### Ops And Edits
37
38
 
38
39
  Data in The Graph is stored as an Op (operation). Ops represent a set of changes applied to entities. A change could be setting or deleting a triple or a relation. Both triples and relations are represented as Ops.
39
40
 
40
- When writing data, these ops are grouped into a logical set called an "Edit." An Edit has a name, authors, and other metadata to represent the set of changes. This edit is then encoded into a binary representation for storage efficiency.
41
+ When writing data, these ops are grouped into a logical set called an edit. An edit has a name, authors, and metadata to represent the set of changes. This edit is encoded into a binary representation before being uploaded to IPFS.
41
42
 
42
43
  [Ops and edits in GRC-20](https://github.com/yanivtal/graph-improvement-proposals/blob/new-ops/grcs/0020-knowledge-graph.md#101-operations-op)
43
44
 
44
45
  ## Using
45
46
 
46
- ### Unique IDs
47
+ The preferred API has two parts:
48
+
49
+ - `Ops` builds GRC-20 operations.
50
+ - `createGeoClient(...)` handles configured workflows such as API calls, uploads, RPC reads, and transaction calldata.
51
+
52
+ ```ts
53
+ import { GeoTestnetConfig, Ops, createGeoClient } from "@geoprotocol/geo-sdk";
54
+
55
+ const geo = createGeoClient({ network: GeoTestnetConfig });
56
+
57
+ const { id: entityId, ops } = Ops.entities.create({
58
+ name: "Test Entity",
59
+ description: "Created with the new API",
60
+ });
61
+
62
+ const tx = await geo.personalSpaces.publishEdit({
63
+ name: "Create Test Entity",
64
+ spaceId,
65
+ author: spaceId,
66
+ ops,
67
+ });
68
+
69
+ await walletClient.sendTransaction({ to: tx.to, data: tx.calldata });
70
+ ```
71
+
72
+ ### Imports
47
73
 
48
- Entities throughout The Graph are referenced via globally unique identifiers. The SDK exposes APIs for creating these IDs.
74
+ Use the root package when convenience matters:
49
75
 
50
76
  ```ts
51
- import { Id } from "@geoprotocol/geo-sdk";
77
+ import { GeoTestnetConfig, Ops, createGeoClient } from "@geoprotocol/geo-sdk";
78
+ ```
79
+
80
+ Subpath exports are available for bundle-sensitive code
52
81
 
53
- const newId = Id.generate();
82
+ ```ts
83
+ import { entities, relations } from "@geoprotocol/geo-sdk/ops";
84
+ import { createGeoClient } from "@geoprotocol/geo-sdk/client";
85
+ import {
86
+ GeoTestnetConfig,
87
+ defineGeoNetworkConfig,
88
+ } from "@geoprotocol/geo-sdk/networks";
54
89
  ```
55
90
 
56
- ### Creating properties, types and entities
91
+ Use `Ops` from `@geoprotocol/geo-sdk` for op builders. Configured workflows live behind `createGeoClient`.
57
92
 
58
- Working with triple and relations ops is a low level API and give you maximum flexibility. In order to ease the process of creating and updating data, the library also exports APIs for creating properties, types and entities.
93
+ ### Network Configuration
94
+
95
+ Use the built-in testnet config:
59
96
 
60
97
  ```ts
61
- import { Graph } from '@geoprotocol/geo-sdk';
98
+ import { GeoTestnetConfig, createGeoClient } from "@geoprotocol/geo-sdk";
99
+
100
+ const geo = createGeoClient({ network: GeoTestnetConfig });
101
+
102
+ console.log(geo.network.id); // "TESTNET"
103
+ ```
104
+
105
+ `network` is required, and the client expects the full config object. Pass `GeoTestnetConfig`, not the string `"TESTNET"`.
106
+
107
+ Define a custom deployment:
62
108
 
63
- // create a property
64
- const propertyResult = Graph.createProperty({
65
- name: 'name of the property',
66
- dataType: 'TEXT', // BOOLEAN | INTEGER | FLOAT | DECIMAL | TEXT | BYTES | DATE | TIME | DATETIME | SCHEDULE | POINT | EMBEDDING | RELATION
109
+ ```ts
110
+ import { createGeoClient, defineGeoNetworkConfig } from "@geoprotocol/geo-sdk";
111
+
112
+ const local = defineGeoNetworkConfig({
113
+ id: "LOCAL",
114
+ name: "Local Geo",
115
+ apiOrigin: "http://localhost:3000",
116
+ chain: {
117
+ id: 31337,
118
+ name: "Anvil",
119
+ rpcUrl: "http://localhost:8545",
120
+ },
121
+ contracts: {
122
+ SPACE_REGISTRY_ADDRESS: "0x0000000000000000000000000000000000000001",
123
+ DAO_SPACE_FACTORY_ADDRESS: "0x0000000000000000000000000000000000000002",
124
+ },
67
125
  });
68
126
 
69
- // create a type
70
- const { id: personTypeId, ops: createPersonTypeOps } = Graph.createType({
71
- name: 'name of the type',
72
- properties: […listOfPropertyIds],
127
+ const geo = createGeoClient({ network: local });
128
+ ```
129
+
130
+ Pass a fetch implementation when your runtime does not provide `globalThis.fetch`, or when you want test isolation:
131
+
132
+ ```ts
133
+ const geo = createGeoClient({
134
+ network: GeoTestnetConfig,
135
+ fetch: customFetch,
73
136
  });
137
+ ```
138
+
139
+ ### Local Contracts V2 Validation
140
+
141
+ When working against the local-geobrowser contracts checkout, deploy that stack
142
+ first so it writes `.deployments.json`, then point the SDK opt-in test at it:
143
+
144
+ ```sh
145
+ cd /path/to/local-geobrowser
146
+ just init-infra
147
+ just init-contract
148
+
149
+ cd /path/to/geo-sdk
150
+ GEO_LOCAL_GEOBROWSER_DEPLOYMENTS=/path/to/local-geobrowser/.deployments.json \
151
+ pnpm exec vitest run src/contracts-v2/local-geobrowser.e2e.test.ts
152
+ ```
153
+
154
+ The test builds a custom `defineGeoNetworkConfig(...)` from the deployment file
155
+ and decodes DAO creation, update-voting-settings proposal, and versioned vote
156
+ calldata against the contracts v2 ABIs.
157
+
158
+ ## Ops API
74
159
 
75
- // create an image
76
- const { id: imageId, ops: createImageOps } = await Graph.createImage({
77
- url: 'https://example.com/image.png',
78
- // blob: new Blob([fs.readFileSync(path.join(__dirname, 'cover.png'))], { type: 'image/png' });
160
+ Ops functions build operation arrays that can be combined before publishing.
161
+
162
+ ```ts
163
+ import type { Op } from "@geoprotocol/geo-sdk";
164
+
165
+ const ops: Op[] = [];
166
+ ops.push(...entityOps);
167
+ ops.push(...relationOps);
168
+ ```
169
+
170
+ ### Properties, Types, And Entities
171
+
172
+ Create a property, a type, and an entity:
173
+
174
+ ```ts
175
+ import { Ops } from "@geoprotocol/geo-sdk";
176
+
177
+ const { id: websitePropertyId, ops: propertyOps } = Ops.properties.create({
178
+ name: "Website",
179
+ dataType: "TEXT",
180
+ });
181
+
182
+ const { id: restaurantTypeId, ops: typeOps } = Ops.types.create({
183
+ name: "Restaurant",
184
+ properties: [websitePropertyId],
79
185
  });
80
186
 
81
- // create an entity
82
- const { id: restaurantId, ops: createRestaurantOps } = Graph.createEntity({
83
- name: 'name of the entity',
84
- description: 'description of the entity',
85
- types: […listOfTypeIds],
86
- cover: imageId,
187
+ const { id: restaurantId, ops: entityOps } = Ops.entities.create({
188
+ name: "Yum Yum",
189
+ description: "A restaurant serving fusion cuisine",
190
+ types: [restaurantTypeId],
87
191
  values: [
88
192
  {
89
- property: propertyId,
90
- type: 'text',
91
- value: 'value of the property',
92
- }
93
- ],
94
- relations: {
95
- // relation property
96
- [relationPropertyId]: {
97
- toEntity: 'id of the entity',
98
- id: 'id of the relation', // optional
99
- position: positionString, // optional
193
+ property: websitePropertyId,
194
+ type: "text",
195
+ value: "https://example.com",
100
196
  },
101
- },
197
+ ],
102
198
  });
103
- ```
104
199
 
105
- #### Typed values
200
+ const ops = [...propertyOps, ...typeOps, ...entityOps];
201
+ ```
106
202
 
107
- Values are passed as typed objects with a `type` field that determines the value format:
203
+ Update an entity:
108
204
 
109
205
  ```ts
110
- import { Graph, Id } from "@geoprotocol/geo-sdk";
111
-
112
- const { id: personId, ops: createPersonOps } = Graph.createEntity({
206
+ const { ops } = Ops.entities.update({
207
+ id: restaurantId,
208
+ name: "Yum Yum Kitchen",
113
209
  values: [
114
- // Text value (with optional language)
115
210
  {
116
- property: someTextPropertyId,
211
+ property: websitePropertyId,
117
212
  type: "text",
118
- value: "Hello",
119
- language: Id("dad6e52a5e944e559411cfe3a3c3ea64"), // optional
120
- },
121
- // Number value — integer (with optional unit)
122
- {
123
- property: someIntPropertyId,
124
- type: "integer",
125
- value: 42,
126
- unit: Id("016c9b1cd8a84e4d9e844e40878bb235"), // optional
213
+ value: "https://yum.example",
127
214
  },
128
- // Number value — float (with optional unit)
215
+ ],
216
+ });
217
+ ```
218
+
219
+ Unset property values:
220
+
221
+ ```ts
222
+ const { ops } = Ops.entities.update({
223
+ id: restaurantId,
224
+ unset: [
225
+ { property: websitePropertyId },
226
+ { property: descriptionPropertyId, language: "all" },
227
+ ],
228
+ });
229
+ ```
230
+
231
+ Create relation properties:
232
+
233
+ ```ts
234
+ const { id: likesPropertyId, ops } = Ops.properties.create({
235
+ name: "Likes",
236
+ dataType: "RELATION",
237
+ relationValueTypes: [restaurantTypeId],
238
+ });
239
+ ```
240
+
241
+ ### Typed Values
242
+
243
+ Entity values are typed objects. The `type` field determines the value shape:
244
+
245
+ ```ts
246
+ import { Ops } from "@geoprotocol/geo-sdk";
247
+
248
+ const { ops } = Ops.entities.create({
249
+ name: "Cafe visit",
250
+ values: [
251
+ { property: namePropertyId, type: "text", value: "Morning Coffee" },
252
+ { property: openPropertyId, type: "boolean", value: true },
253
+ { property: visitsPropertyId, type: "integer", value: 42 },
254
+ { property: ratingPropertyId, type: "float", value: 4.8 },
255
+ { property: openedDatePropertyId, type: "date", value: "2024-01-15" },
256
+ { property: opensAtPropertyId, type: "time", value: "08:30:00Z" },
129
257
  {
130
- property: someFloatPropertyId,
131
- type: "float",
132
- value: 42.5,
133
- unit: Id("016c9b1cd8a84e4d9e844e40878bb235"), // optional
258
+ property: reviewedAtPropertyId,
259
+ type: "datetime",
260
+ value: "2024-01-15T14:30:00Z",
134
261
  },
135
- // Boolean value
136
262
  {
137
- property: someBooleanPropertyId,
138
- type: "boolean",
139
- value: true,
263
+ property: schedulePropertyId,
264
+ type: "schedule",
265
+ value: "FREQ=WEEKLY;BYDAY=MO,WE,FR",
140
266
  },
141
- // Point value (with optional altitude)
142
267
  {
143
- property: somePointPropertyId,
268
+ property: locationPropertyId,
144
269
  type: "point",
145
270
  lon: -122.4194,
146
271
  lat: 37.7749,
147
- alt: 10.5, // optional
148
- },
149
- // Date value (ISO 8601 format: YYYY-MM-DD)
150
- {
151
- property: someDatePropertyId,
152
- type: "date",
153
- value: "2024-01-15",
154
- },
155
- // Time value (ISO 8601 format with timezone)
156
- {
157
- property: someTimePropertyId,
158
- type: "time",
159
- value: "14:30:00Z",
160
- },
161
- // Datetime value (ISO 8601 combined format)
162
- {
163
- property: someDatetimePropertyId,
164
- type: "datetime",
165
- value: "2024-01-15T14:30:00Z",
166
272
  },
167
- // Schedule value (iCalendar RRULE format)
168
273
  {
169
- property: someSchedulePropertyId,
170
- type: "schedule",
171
- value: "FREQ=WEEKLY;BYDAY=MO,WE,FR",
274
+ property: bytesPropertyId,
275
+ type: "bytes",
276
+ value: new Uint8Array([1, 2, 3]),
172
277
  },
173
278
  ],
174
279
  });
175
280
  ```
176
281
 
177
- #### Example Flow
282
+ ### Relations
178
283
 
179
- ```ts
180
- import { Graph, type Op } from "@geoprotocol/geo-sdk";
284
+ Create, update, and delete relation entities:
181
285
 
182
- const ops: Array<Op> = [];
286
+ ```ts
287
+ import { Ops, Position } from "@geoprotocol/geo-sdk";
183
288
 
184
- // create an age property
185
- const { id: agePropertyId, ops: createAgePropertyOps } = Graph.createProperty({
186
- dataType: "INTEGER",
187
- name: "Age",
289
+ const { id: relationId, ops: createRelationOps } = Ops.relations.create({
290
+ fromEntity: personId,
291
+ toEntity: restaurantId,
292
+ type: likesPropertyId,
293
+ position: Position.generate(),
188
294
  });
189
- ops.push(...createAgePropertyOps);
190
295
 
191
- // create a likes property
192
- const { id: likesPropertyId, ops: createLikesPropertyOps } =
193
- Graph.createProperty({
194
- dataType: "RELATION",
195
- name: "Likes",
196
- });
197
- ops.push(...createLikesPropertyOps);
296
+ const { ops: updateRelationOps } = Ops.relations.update({
297
+ id: relationId,
298
+ position: Position.generateBetween(previousPosition, nextPosition),
299
+ });
198
300
 
199
- // create a person type
200
- const { id: personTypeId, ops: createPersonTypeOps } = Graph.createType({
201
- name: "Person",
202
- cover: personCoverId,
203
- properties: [agePropertyId, likesPropertyId],
301
+ const { ops: deleteRelationOps } = Ops.relations.delete({
302
+ id: relationId,
204
303
  });
205
- ops.push(...createPersonTypeOps);
304
+ ```
206
305
 
207
- // create a restaurant cover image
208
- const { id: restaurantCoverId, ops: createRestaurantCoverOps } =
209
- await Graph.createImage({
210
- url: "https://example.com/image.png",
211
- });
212
- ops.push(...createRestaurantCoverOps);
306
+ Relations can also carry their own entity values:
213
307
 
214
- // create a restaurant entity with a website property
215
- const restaurantTypeId = "a1b2c3d4e5f647889012345678abcdef";
216
- const { id: restaurantId, ops: createRestaurantOps } = Graph.createEntity({
217
- name: "Yum Yum",
218
- description: "A restaurant serving fusion cuisine",
219
- cover: restaurantCoverId,
220
- types: [restaurantTypeId],
221
- values: [
308
+ ```ts
309
+ const { ops } = Ops.relations.create({
310
+ fromEntity: personId,
311
+ toEntity: companyId,
312
+ type: teamMemberPropertyId,
313
+ entityName: "Team member",
314
+ entityValues: [
222
315
  {
223
- property: WEBSITE_PROPERTY,
224
- type: "text",
225
- value: "https://example.com",
316
+ property: joinedDatePropertyId,
317
+ type: "date",
318
+ value: "2024-05-01",
226
319
  },
227
320
  ],
228
321
  });
229
- ops.push(...createRestaurantOps);
322
+ ```
230
323
 
231
- // create a person cover image
232
- const { id: personCoverId, ops: createPersonCoverOps } =
233
- await Graph.createImage({
234
- url: "https://example.com/avatar.png",
235
- });
236
- ops.push(...createPersonCoverOps);
324
+ ### Images
237
325
 
238
- // create a person entity with a likes relation to the restaurant entity
239
- const { id: personId, ops: createPersonOps } = Graph.createEntity({
240
- name: "Jane Doe",
241
- types: [personTypeId],
242
- cover: personCoverId,
243
- values: [
326
+ Image creation goes through `geo.images.create(...)`. The configured client uploads the image, detects dimensions when possible, and returns the image entity ops.
327
+
328
+ ### Comments
329
+
330
+ Create a comment on an entity:
331
+
332
+ ```ts
333
+ import { Ops } from "@geoprotocol/geo-sdk";
334
+
335
+ const { id: commentId, ops } = Ops.comments.create({
336
+ content: "Looks good to me.",
337
+ replyTo: {
338
+ entityId,
339
+ spaceId,
340
+ },
341
+ });
342
+ ```
343
+
344
+ Create a nested comment when you already have reply-chain context:
345
+
346
+ ```ts
347
+ const { ops } = Ops.comments.create({
348
+ content: "Replying to the parent comment.",
349
+ replyTo: {
350
+ entityId: parentCommentId,
351
+ spaceId,
352
+ },
353
+ replyToRelations: [
244
354
  {
245
- property: agePropertyId,
246
- type: "integer",
247
- value: 42,
355
+ entityId: rootEntityId,
356
+ spaceId,
357
+ position: "a0",
248
358
  },
249
359
  ],
250
- relations: {
251
- [likesPropertyId]: {
252
- toEntity: restaurantId,
253
- },
360
+ });
361
+ ```
362
+
363
+ Update a comment:
364
+
365
+ ```ts
366
+ const { ops } = Ops.comments.update({
367
+ id: commentId,
368
+ content: "Updated comment text.",
369
+ resolved: true,
370
+ });
371
+ ```
372
+
373
+ Use `geo.comments.create(...)` when reply-chain context should be fetched from the configured API.
374
+
375
+ ### Proposal Reviews
376
+
377
+ Create and update proposal review ops:
378
+
379
+ ```ts
380
+ import { Ops } from "@geoprotocol/geo-sdk";
381
+
382
+ const { id: reviewId, ops: createReviewOps } = Ops.proposalReviews.create({
383
+ proposal: {
384
+ id: proposalId,
385
+ name: "Improve restaurant data",
254
386
  },
387
+ pass: true,
388
+ content: "The proposal is complete and accurate.",
389
+ completeness: 1,
390
+ accuracy: 0.8,
391
+ skill: 0.8,
392
+ effort: 0.6,
393
+ });
394
+
395
+ const { ops: updateReviewOps } = Ops.proposalReviews.update({
396
+ proposalReviewId: reviewId,
397
+ pass: false,
398
+ content: "Needs more sources.",
255
399
  });
256
- ops.push(...createPersonOps);
257
400
  ```
258
401
 
259
- ### Updating entities
402
+ ## Client API
260
403
 
261
- Update an entity's name, description, and property values. Also supports unsetting properties.
404
+ Create a configured client once and use it for workflows that need API origins, uploads, RPC URLs, or contract addresses.
262
405
 
263
406
  ```ts
264
- import { Graph } from "@geoprotocol/geo-sdk";
407
+ import { GeoTestnetConfig, Ops, createGeoClient } from "@geoprotocol/geo-sdk";
265
408
 
266
- // Update values
267
- const { ops: updateOps } = Graph.updateEntity({
268
- id: entityId,
269
- name: "Updated Name",
270
- description: "Updated description",
271
- values: [
272
- {
273
- property: agePropertyId,
274
- type: "integer",
275
- value: 43,
276
- },
277
- ],
409
+ const geo = createGeoClient({ network: GeoTestnetConfig });
410
+ ```
411
+
412
+ ### `geo.api`
413
+
414
+ Use `geo.api.graphql(...)` for low-level GraphQL requests:
415
+
416
+ ```ts
417
+ const response = await geo.api.graphql<{
418
+ entity: { id: string; name: string | null } | null;
419
+ }>(`
420
+ query {
421
+ entity(id: "3af3e22d21694a078681516710b7ecf1") {
422
+ id
423
+ name
424
+ }
425
+ }
426
+ `);
427
+
428
+ if (response.errors) {
429
+ console.error(response.errors);
430
+ }
431
+ ```
432
+
433
+ Use `geo.api.getEditCalldata(...)` when calldata must come from the API:
434
+
435
+ ```ts
436
+ const { to, data } = await geo.api.getEditCalldata({
437
+ spaceId,
438
+ cid: "ipfs://bafkreihdwdcefgh4dqkjv67uzcmw7ojee6xedzdetojuzjevtenxquvyku",
278
439
  });
279
440
 
280
- // Unset property values
281
- const { ops: unsetOps } = Graph.updateEntity({
282
- id: entityId,
283
- unset: [
284
- { property: propertyId }, // unset all languages
285
- { property: propertyId2, language: { type: "all" } }, // explicit all languages
286
- ],
441
+ await walletClient.sendTransaction({ to, data });
442
+ ```
443
+
444
+ For personal-space edits, prefer `geo.personalSpaces.publishEdit(...)`.
445
+
446
+ ### `geo.images`
447
+
448
+ Upload an image and build image entity ops in one call:
449
+
450
+ ```ts
451
+ const imageFromUrl = await geo.images.create({
452
+ url: "https://example.com/cover.png",
453
+ name: "Cover image",
454
+ description: "Image used as the space cover.",
455
+ });
456
+
457
+ console.log(imageFromUrl.id);
458
+ console.log(imageFromUrl.cid);
459
+ console.log(imageFromUrl.ops);
460
+ ```
461
+
462
+ You can also pass a `Blob` directly:
463
+
464
+ ```ts
465
+ const file = new Blob([imageBytes], { type: "image/png" });
466
+
467
+ const imageFromBlob = await geo.images.create({
468
+ blob: file,
469
+ name: "Cover image",
287
470
  });
471
+
472
+ console.log(imageFromBlob.cid);
288
473
  ```
289
474
 
290
- ### Deleting entities
475
+ ### `geo.storage`
291
476
 
292
- Delete an entity by removing all its values and relations in a specific space. This is an async operation that queries the API to discover what to delete.
477
+ Use `geo.images.create(...)` when you want to upload an image and create image entity ops in one call. Use `geo.storage.uploadImage(...)` only when you need the raw uploaded CID and detected dimensions without graph ops:
293
478
 
294
479
  ```ts
295
- import { Graph } from "@geoprotocol/geo-sdk";
480
+ const uploaded = await geo.storage.uploadImage({
481
+ url: "https://example.com/photo.png",
482
+ });
296
483
 
297
- const { ops: deleteOps } = await Graph.deleteEntity({
484
+ console.log(uploaded.cid);
485
+ console.log(uploaded.dimensions);
486
+ ```
487
+
488
+ Upload CSV content:
489
+
490
+ ```ts
491
+ const cid = await geo.storage.uploadCSV(`name,score
492
+ Alice,10
493
+ Bob,8`);
494
+ ```
495
+
496
+ ### `geo.entities`
497
+
498
+ Delete an entity from a space:
499
+
500
+ ```ts
501
+ const { ops } = await geo.entities.delete({
298
502
  id: entityId,
299
- spaceId: spaceId,
300
- network: "TESTNET", // optional, defaults to "TESTNET"
503
+ spaceId,
301
504
  });
302
505
  ```
303
506
 
304
- > **Note:** `deleteEntity` queries the API to discover the entity's properties and relations, then generates ops to unset all values and delete all relations. The entity itself transitions to an empty state rather than being permanently destroyed.
507
+ Entity deletion queries the configured API for current values and relations in the target space, then builds ops to unset those values and delete those relations. The entity transitions to an empty state in that space; it is not globally destroyed.
305
508
 
306
- ### Relations
509
+ ### `geo.comments`
307
510
 
308
- Relations describe edges between entities in the knowledge graph. Each relation is itself an entity, which means relations can have their own properties (e.g., a "Team Member" relation could have a "joined date" property).
511
+ Create a comment while preserving reply-to chains:
309
512
 
310
513
  ```ts
311
- import { Graph, Position } from "@geoprotocol/geo-sdk";
514
+ const comment = await geo.comments.create({
515
+ content: "This should be easier to find.",
516
+ replyTo: {
517
+ entityId,
518
+ spaceId,
519
+ },
520
+ });
521
+ ```
312
522
 
313
- // Create a relation
314
- const { id: relationId, ops: createRelOps } = Graph.createRelation({
315
- fromEntity: personId,
316
- toEntity: restaurantId,
317
- type: likesPropertyId, // the relation type property
318
- position: Position.generateBetween(), // optional ordering
523
+ Reply to another comment:
524
+
525
+ ```ts
526
+ const reply = await geo.comments.create({
527
+ content: "Following up here.",
528
+ replyTo: {
529
+ entityId: comment.id,
530
+ spaceId,
531
+ },
319
532
  });
533
+ ```
320
534
 
321
- // Update a relation (change position)
322
- const { ops: updateRelOps } = Graph.updateRelation({
323
- id: relationId,
324
- position: Position.generateBetween(posA, posB),
535
+ Update a comment:
536
+
537
+ ```ts
538
+ const { ops } = geo.comments.update({
539
+ id: comment.id,
540
+ content: "Updated comment text.",
541
+ resolved: true,
325
542
  });
543
+ ```
326
544
 
327
- // Delete a relation
328
- const { ops: deleteRelOps } = Graph.deleteRelation({
329
- id: relationId,
545
+ ### `geo.personalSpaces`
546
+
547
+ Check whether an address already has a personal space:
548
+
549
+ ```ts
550
+ const hasExistingSpace = await geo.personalSpaces.hasSpace({
551
+ address: account.address,
330
552
  });
331
553
  ```
332
554
 
333
- ### Positions
555
+ Create a personal space:
556
+
557
+ ```ts
558
+ const createSpace = geo.personalSpaces.create({
559
+ name: "Alice",
560
+ accountAddress: account.address,
561
+ });
334
562
 
335
- The `Position` module provides fractional indexing for ordering entities, relations, and other ordered items without renumbering.
563
+ await walletClient.sendTransaction({
564
+ to: createSpace.to,
565
+ data: createSpace.calldata,
566
+ });
567
+ ```
568
+
569
+ `geo.personalSpaces.create(...)` also returns initial profile ops. After the create-space transaction is mined and you know the new `spaceId`, publish those ops into the space and immediately set the generated space entity as the space topic:
336
570
 
337
571
  ```ts
338
- import { Position } from "@geoprotocol/geo-sdk";
572
+ const profileTx = await geo.personalSpaces.publishEdit({
573
+ name: "Create personal space profile",
574
+ spaceId,
575
+ author: spaceId,
576
+ ops: createSpace.ops,
577
+ });
339
578
 
340
- // Generate a position (for first item)
341
- const pos1 = Position.generate();
579
+ await walletClient.sendTransaction({
580
+ to: profileTx.to,
581
+ data: profileTx.calldata,
582
+ });
342
583
 
343
- // Generate a position between two existing positions
344
- const between = Position.generateBetween(pos1, pos2);
584
+ const topicTx = geo.personalSpaces.setTopic({
585
+ spaceId,
586
+ topicId: createSpace.spaceEntityId,
587
+ });
345
588
 
346
- // Generate at the start (before first item)
347
- const first = Position.generateBetween(null, pos1);
589
+ await walletClient.sendTransaction({
590
+ to: topicTx.to,
591
+ data: topicTx.calldata,
592
+ });
593
+ ```
348
594
 
349
- // Generate at the end (after last item)
350
- const last = Position.generateBetween(pos1, null);
595
+ Publish any edit to a personal space:
351
596
 
352
- // Compare positions
353
- const result = Position.compare(posA, posB); // -1, 0, or 1
597
+ ```ts
598
+ const { ops } = Ops.entities.create({
599
+ name: "Test Entity",
600
+ });
354
601
 
355
- // Sort an array of positions
356
- const sorted = Position.sort([pos3, pos1, pos2]);
602
+ const tx = await geo.personalSpaces.publishEdit({
603
+ name: "Create Test Entity",
604
+ spaceId,
605
+ author: spaceId,
606
+ ops,
607
+ });
608
+
609
+ await walletClient.sendTransaction({
610
+ to: tx.to,
611
+ data: tx.calldata,
612
+ });
357
613
  ```
358
614
 
359
- ### Publishing an edit onchain using your Geo Account
615
+ The `author` field is the author's personal space ID, not a wallet address.
360
616
 
361
- The Geo Genesis browser uses a smart account associated with your account to publish edits. There may be situations where you want to use the same account in your code as you do on Geo Genesis. In order to get the smart account wallet client you can use the `getSmartAccountWalletClient` function.
617
+ ### `geo.daoSpaces`
362
618
 
363
- To use `getSmartAccountWalletClient` you'll need the private key associated with your Geo account. You can get your private key using https://www.geobrowser.io/export-wallet.
619
+ DAO helpers target the contracts v2 surface. Voting settings use percentages
620
+ and days on the SDK side; the SDK maps them to ratio-base integers and seconds
621
+ for the contract. Use a network config whose DAO contract addresses point at a
622
+ contracts v2 deployment.
364
623
 
365
- Transaction costs from your smart account will be sponsored by the Geo team for the duration of the early access period. Eventually you will need to provide your own API key or provide funds to your smart account.
624
+ ```ts
625
+ const votingSettings = {
626
+ partialPercentageSupportThreshold: 50,
627
+ universalPercentageSupportThreshold: 90,
628
+ flatSupportThreshold: 1,
629
+ quorum: 1,
630
+ durationInDays: 2,
631
+ disableFastPathAccessForNewMembers: true,
632
+ executionGracePeriodInDays: 14,
633
+ };
634
+ ```
635
+
636
+ Create a DAO space:
366
637
 
367
638
  ```ts
368
- import { getSmartAccountWalletClient } from "@geoprotocol/geo-sdk";
639
+ const tx = await geo.daoSpaces.create({
640
+ name: "Research DAO",
641
+ author: authorSpaceId,
642
+ initialEditorSpaceIds: [authorSpaceId],
643
+ initialMemberSpaceIds: [authorSpaceId],
644
+ votingSettings,
645
+ });
369
646
 
370
- // IMPORTANT: Be careful with your private key. Don't commit it to version control.
371
- // You can get your private key using https://www.geobrowser.io/export-wallet
372
- const privateKey = `0x${privateKeyFromGeoWallet}`;
373
- const smartAccountWalletClient = await getSmartAccountWalletClient({
374
- privateKey,
375
- // rpcUrl, // optional
647
+ await walletClient.sendTransaction({
648
+ to: tx.to,
649
+ data: tx.calldata,
376
650
  });
377
651
  ```
378
652
 
379
- ### Personal Spaces
653
+ Include extra initial ops in the DAO creation edit:
654
+
655
+ ```ts
656
+ const { ops } = Ops.entities.create({
657
+ name: "DAO Welcome Page",
658
+ });
380
659
 
381
- Personal spaces are owned by a single address and don't require voting for governance. The SDK provides a `personalSpace` module with helper functions for creating and publishing to personal spaces.
660
+ const tx = await geo.daoSpaces.create({
661
+ name: "Research DAO",
662
+ author: authorSpaceId,
663
+ initialEditorSpaceIds: [authorSpaceId],
664
+ votingSettings,
665
+ ops,
666
+ });
667
+ ```
382
668
 
383
- #### Creating a personal space
669
+ Propose an edit to a DAO space:
384
670
 
385
671
  ```ts
386
- import { personalSpace, getWalletClient } from "@geoprotocol/geo-sdk";
672
+ const { ops } = Ops.entities.update({
673
+ id: entityId,
674
+ name: "Updated entity name",
675
+ });
387
676
 
388
- const walletClient = await getWalletClient({
389
- privateKey: addressPrivateKey,
677
+ const proposal = await geo.daoSpaces.proposeEdit({
678
+ name: "Update entity name",
679
+ ops,
680
+ author: authorSpaceId,
681
+ daoSpaceAddress,
682
+ callerSpaceId: authorSpaceId,
683
+ daoSpaceId,
684
+ votingMode: "FAST",
390
685
  });
391
686
 
392
- // Get the calldata for creating a personal space
393
- const { to, calldata } = personalSpace.createSpace();
687
+ await walletClient.sendTransaction({
688
+ to: proposal.to,
689
+ data: proposal.calldata,
690
+ });
691
+ ```
394
692
 
395
- // Submit the transaction
396
- const txHash = await walletClient.sendTransaction({
397
- account: walletClient.account,
398
- to,
399
- data: calldata,
693
+ Create a new version of an existing edit proposal:
694
+
695
+ ```ts
696
+ const updatedProposal = await geo.daoSpaces.proposeEdit({
697
+ name: "Update entity name, revision 2",
698
+ ops,
699
+ author: authorSpaceId,
700
+ daoSpaceAddress,
701
+ callerSpaceId: authorSpaceId,
702
+ daoSpaceId,
703
+ proposalId,
704
+ updateProposal: true,
705
+ versionId: 2,
400
706
  });
401
707
  ```
402
708
 
403
- #### Publishing to a personal space
709
+ If `updateProposal: true` is set and `versionId` is omitted, the SDK reads
710
+ `latestProposalVersion(...)` from the configured network RPC before uploading
711
+ the edit. Pass `versionId` when you already know the next version or when your
712
+ custom network config has no RPC URL.
713
+
714
+ Manage DAO members and editors:
404
715
 
405
716
  ```ts
406
- import { personalSpace } from "@geoprotocol/geo-sdk";
717
+ const addMember = geo.daoSpaces.proposeAddMember({
718
+ authorSpaceId,
719
+ spaceId: daoSpaceId,
720
+ daoSpaceAddress,
721
+ newMemberSpaceId: memberSpaceId,
722
+ votingMode: "FAST",
723
+ });
407
724
 
408
- const { cid, editId, to, calldata } = await personalSpace.publishEdit({
409
- name: "My Edit",
410
- spaceId, // your personal space ID (dashless hex UUID)
411
- ops, // array of Op from Graph.* functions
412
- author: spaceId, // your personal space ID (same as spaceId for personal spaces)
413
- network: "TESTNET",
725
+ const removeMember = geo.daoSpaces.proposeRemoveMember({
726
+ authorSpaceId,
727
+ spaceId: daoSpaceId,
728
+ daoSpaceAddress,
729
+ memberToRemoveSpaceId: memberSpaceId,
730
+ });
731
+
732
+ const addEditor = geo.daoSpaces.proposeAddEditor({
733
+ authorSpaceId,
734
+ spaceId: daoSpaceId,
735
+ daoSpaceAddress,
736
+ newEditorSpaceId: editorSpaceId,
414
737
  });
415
738
 
416
- const txHash = await walletClient.sendTransaction({ to, data: calldata });
739
+ const removeEditor = geo.daoSpaces.proposeRemoveEditor({
740
+ authorSpaceId,
741
+ spaceId: daoSpaceId,
742
+ daoSpaceAddress,
743
+ editorToRemoveSpaceId: editorSpaceId,
744
+ });
417
745
  ```
418
746
 
419
- > **Important:** The `author` field must be a **personal space ID** (dashless hex UUID), not a wallet address or entity ID. For personal spaces, this is the same as `spaceId`. Verified from SDK source: `personal-space/types.ts` defines author as `/** The author's personal space ID. */`.
747
+ Editor changes only support `SLOW` voting. Member changes can use the default or an explicit `votingMode`.
748
+
749
+ Propose voting-settings changes:
420
750
 
421
- ### DAO Spaces
751
+ ```ts
752
+ const updateVoting = geo.daoSpaces.proposeUpdateVotingSettings({
753
+ authorSpaceId,
754
+ spaceId: daoSpaceId,
755
+ daoSpaceAddress,
756
+ votingSettings: {
757
+ partialPercentageSupportThreshold: 60,
758
+ universalPercentageSupportThreshold: 95,
759
+ flatSupportThreshold: 2,
760
+ quorum: 2,
761
+ durationInDays: 5,
762
+ disableFastPathAccessForNewMembers: true,
763
+ executionGracePeriodInDays: 14,
764
+ },
765
+ });
766
+ ```
422
767
 
423
- DAO spaces use governance (voting) for publishing changes. The SDK provides a `daoSpace` module for proposing edits and managing membership.
768
+ Updating voting settings only supports `SLOW` voting.
424
769
 
425
- #### Proposing an edit to a DAO space
770
+ Request membership in a DAO space:
426
771
 
427
772
  ```ts
428
- import { daoSpace, Graph } from "@geoprotocol/geo-sdk";
773
+ const request = geo.daoSpaces.proposeRequestMembership({
774
+ authorSpaceId: requesterSpaceId,
775
+ spaceId: daoSpaceId,
776
+ });
777
+
778
+ await walletClient.sendTransaction({
779
+ to: request.to,
780
+ data: request.calldata,
781
+ });
782
+ ```
429
783
 
430
- const { ops } = Graph.createEntity({ name: "New Entity" });
784
+ DAO proposal IDs and DAO space IDs are bytes16 hex strings, usually `0x` plus 32 hex characters.
431
785
 
432
- const { editId, cid, to, calldata, proposalId } = await daoSpace.proposeEdit({
433
- name: "Add new entity",
434
- ops,
435
- author: callerSpaceId, // your personal space ID (as `0x${string}`)
436
- daoSpaceAddress: "0xDAOSpaceContractAddress", // the DAO space contract address
437
- callerSpaceId: "0xCallerBytes16SpaceId", // your personal space ID (bytes16 hex)
438
- daoSpaceId: "0xDAOBytes16SpaceId", // the DAO space ID (bytes16 hex)
439
- network: "TESTNET",
786
+ Vote on a proposal:
787
+
788
+ ```ts
789
+ const vote = geo.daoSpaces.voteProposal({
790
+ authorSpaceId,
791
+ spaceId: daoSpaceId,
792
+ proposalId,
793
+ versionId: 1,
794
+ vote: "YES",
440
795
  });
441
796
 
442
- await walletClient.sendTransaction({ to, data: calldata });
797
+ await walletClient.sendTransaction({
798
+ to: vote.to,
799
+ data: vote.calldata,
800
+ });
443
801
  ```
444
802
 
445
- > **Note:** `callerSpaceId` and `daoSpaceId` must be `0x`-prefixed bytes16 hex strings (e.g., `0x` + 32 hex chars). `author` is your personal space ID, also `0x`-prefixed.
803
+ `versionId` defaults to `1`. Pass it explicitly when voting on a later proposal
804
+ version.
446
805
 
447
- #### Requesting membership in a DAO space
806
+ Execute a passed proposal:
448
807
 
449
808
  ```ts
450
- import { daoSpace } from "@geoprotocol/geo-sdk";
809
+ const execute = geo.daoSpaces.executeProposal({
810
+ authorSpaceId,
811
+ spaceId: daoSpaceId,
812
+ proposalId,
813
+ });
814
+
815
+ await walletClient.sendTransaction({
816
+ to: execute.to,
817
+ data: execute.calldata,
818
+ });
819
+ ```
451
820
 
452
- const { to, calldata, proposalId } = daoSpace.proposeRequestMembership({
453
- requesterSpaceId: "0xRequesterPersonalSpaceId",
454
- daoSpaceAddress: "0xDAOSpaceContractAddress",
455
- daoSpaceId: "0xDAOBytes16SpaceId",
821
+ ### `geo.entityVotes`
822
+
823
+ Upvote, downvote, or withdraw a vote on an entity:
824
+
825
+ ```ts
826
+ const upvote = geo.entityVotes.upvote({
827
+ authorSpaceId,
828
+ spaceId,
829
+ entityId,
456
830
  });
457
831
 
458
- await walletClient.sendTransaction({ to, data: calldata });
832
+ const downvote = geo.entityVotes.downvote({
833
+ authorSpaceId,
834
+ spaceId,
835
+ entityId,
836
+ });
837
+
838
+ const withdraw = geo.entityVotes.withdraw({
839
+ authorSpaceId,
840
+ spaceId,
841
+ entityId,
842
+ });
459
843
  ```
460
844
 
461
- ## Full Publishing Flow with Smart Account
845
+ Submit any returned transaction with your wallet client:
462
846
 
463
- This example shows the complete flow for publishing an edit using a Geo smart account (Safe with Pimlico paymaster) on testnet. Gas is sponsored, so no testnet ETH is required.
847
+ ```ts
848
+ await walletClient.sendTransaction({
849
+ to: upvote.to,
850
+ data: upvote.calldata,
851
+ });
852
+ ```
853
+
854
+ ## Full Publishing Flow With A Smart Account
464
855
 
465
- The smart account address must already have a personal space. You can create one via the [Geo Genesis browser](https://www.geobrowser.io).
856
+ This example publishes an edit to an existing personal space using a Geo smart account.
466
857
 
467
858
  ```ts
468
- import { createPublicClient, type Hex, http } from "viem";
859
+ import { createPublicClient, http, type Hex } from "viem";
469
860
  import {
470
- Graph,
471
- personalSpace,
861
+ GeoTestnetConfig,
862
+ Ops,
863
+ createGeoClient,
472
864
  getSmartAccountWalletClient,
473
- TESTNET_RPC_URL,
474
865
  } from "@geoprotocol/geo-sdk";
475
866
  import { SpaceRegistryAbi } from "@geoprotocol/geo-sdk/abis";
476
- import { TESTNET } from "@geoprotocol/geo-sdk/contracts";
477
867
 
478
- // IMPORTANT: Be careful with your private key. Don't commit it to version control.
479
- // You can get your private key using https://www.geobrowser.io/export-wallet
480
868
  const privateKey = `0x${privateKeyFromGeoWallet}` as `0x${string}`;
481
-
482
- // Get smart account wallet client (Safe + Pimlico paymaster)
869
+ const geo = createGeoClient({ network: GeoTestnetConfig });
483
870
  const smartAccount = await getSmartAccountWalletClient({ privateKey });
484
- const smartAccountAddress = smartAccount.account.address;
871
+ const accountAddress = smartAccount.account.address;
485
872
 
486
- // Check if a personal space exists for this smart account address
487
- const hasExistingSpace = await personalSpace.hasSpace({
488
- address: smartAccountAddress,
873
+ const hasSpace = await geo.personalSpaces.hasSpace({
874
+ address: accountAddress,
489
875
  });
490
- if (!hasExistingSpace) {
491
- throw new Error("No personal space found for this smart account address.");
876
+
877
+ if (!hasSpace) {
878
+ throw new Error("No personal space found for this account.");
879
+ }
880
+
881
+ const spaceRegistryAddress = GeoTestnetConfig.contracts?.SPACE_REGISTRY_ADDRESS;
882
+ const rpcUrl = GeoTestnetConfig.chain?.rpcUrl;
883
+ if (!spaceRegistryAddress || !rpcUrl) {
884
+ throw new Error("GeoTestnetConfig is missing registry or RPC configuration.");
492
885
  }
493
886
 
494
887
  const publicClient = createPublicClient({
495
- transport: http(TESTNET_RPC_URL),
888
+ transport: http(rpcUrl),
496
889
  });
497
890
 
498
- // Look up the space ID for this smart account address
499
891
  const spaceIdHex = (await publicClient.readContract({
500
- address: TESTNET.SPACE_REGISTRY_ADDRESS,
892
+ address: spaceRegistryAddress,
501
893
  abi: SpaceRegistryAbi,
502
894
  functionName: "addressToSpaceId",
503
- args: [smartAccountAddress],
895
+ args: [accountAddress],
504
896
  })) as Hex;
505
897
 
506
- // Convert bytes16 hex to UUID string (without dashes)
507
898
  const spaceId = spaceIdHex.slice(2, 34).toLowerCase();
508
- console.log("spaceId", spaceId);
509
899
 
510
- // Create an entity
511
- const { ops, id: entityId } = Graph.createEntity({
900
+ const { id: entityId, ops } = Ops.entities.create({
512
901
  name: "Test Entity",
902
+ description: "Created via Geo SDK",
513
903
  });
514
- console.log("entityId", entityId);
515
904
 
516
- // Publish to IPFS and get calldata for on-chain submission
517
- const { cid, editId, to, calldata } = await personalSpace.publishEdit({
518
- name: "Test Edit",
905
+ const tx = await geo.personalSpaces.publishEdit({
906
+ name: "Create Test Entity",
519
907
  spaceId,
520
- ops,
521
908
  author: spaceId,
522
- network: "TESTNET",
909
+ ops,
523
910
  });
524
- console.log("cid", cid);
525
- console.log("editId", editId);
526
911
 
527
- // Send transaction via smart account (account and chain are baked in)
528
912
  const txHash = await smartAccount.sendTransaction({
529
- to,
530
- data: calldata,
913
+ to: tx.to,
914
+ data: tx.calldata,
531
915
  });
532
- console.log("txHash", txHash);
533
916
 
534
- const receipt = await publicClient.waitForTransactionReceipt({
535
- hash: txHash,
536
- });
537
- console.log("Successfully published edit to space", spaceId);
917
+ await publicClient.waitForTransactionReceipt({ hash: txHash });
918
+ console.log("Published entity", entityId, "to space", spaceId);
538
919
  ```
539
920
 
540
- ## Full Publishing Flow (EOA Wallet)
921
+ ## Full Personal Space Creation Flow
541
922
 
542
- This example shows the complete flow for creating a personal space and publishing an edit on testnet using the `personalSpace` module with an EOA wallet.
923
+ This example creates a personal space with an EOA wallet and then publishes the initial profile ops returned by `geo.personalSpaces.create(...)`.
543
924
 
544
925
  ```ts
545
- import { createPublicClient, type Hex, http } from "viem";
926
+ import { createPublicClient, http, type Hex } from "viem";
546
927
  import { privateKeyToAccount } from "viem/accounts";
547
928
  import {
548
- Graph,
549
- personalSpace,
929
+ GeoTestnetConfig,
930
+ createGeoClient,
550
931
  getWalletClient,
551
- TESTNET_RPC_URL,
552
932
  } from "@geoprotocol/geo-sdk";
553
933
  import { SpaceRegistryAbi } from "@geoprotocol/geo-sdk/abis";
554
- import { TESTNET } from "@geoprotocol/geo-sdk/contracts";
555
934
 
556
- // IMPORTANT: Be careful with your private key. Don't commit it to version control.
557
- // You can get your private key using https://www.geobrowser.io/export-wallet
558
- const addressPrivateKey = "0xTODO" as `0x${string}`;
559
- const { address } = privateKeyToAccount(addressPrivateKey);
935
+ const privateKey = "0xTODO" as `0x${string}`;
936
+ const account = privateKeyToAccount(privateKey);
937
+ const geo = createGeoClient({ network: GeoTestnetConfig });
560
938
 
561
- // Take the address and enter it in Faucet to get some testnet ETH https://faucet.conduit.xyz/geo-test-zc16z3tcvf
562
-
563
- // Get wallet client for testnet
564
939
  const walletClient = await getWalletClient({
565
- privateKey: addressPrivateKey,
940
+ privateKey,
566
941
  });
567
942
 
568
- const account = walletClient.account;
943
+ const rpcUrl = GeoTestnetConfig.chain?.rpcUrl;
944
+ const spaceRegistryAddress = GeoTestnetConfig.contracts?.SPACE_REGISTRY_ADDRESS;
945
+ if (!rpcUrl || !spaceRegistryAddress) {
946
+ throw new Error("GeoTestnetConfig is missing registry or RPC configuration.");
947
+ }
569
948
 
570
949
  const publicClient = createPublicClient({
571
- transport: http(TESTNET_RPC_URL),
950
+ transport: http(rpcUrl),
572
951
  });
573
952
 
574
- // Check if a personal space already exists for this address
575
- const hasExistingSpace = await personalSpace.hasSpace({
953
+ const hasSpace = await geo.personalSpaces.hasSpace({
576
954
  address: account.address,
577
955
  });
578
956
 
579
- // Create a personal space if one doesn't exist
580
- if (!hasExistingSpace) {
581
- console.log("Creating personal space...");
957
+ let spaceIdHex = (await publicClient.readContract({
958
+ address: spaceRegistryAddress,
959
+ abi: SpaceRegistryAbi,
960
+ functionName: "addressToSpaceId",
961
+ args: [account.address],
962
+ })) as Hex;
582
963
 
583
- const { to, calldata } = personalSpace.createSpace();
964
+ if (!hasSpace) {
965
+ const createSpace = geo.personalSpaces.create({
966
+ name: "Alice",
967
+ accountAddress: account.address,
968
+ });
584
969
 
585
- const createSpaceTxHash = await walletClient.sendTransaction({
970
+ const createSpaceHash = await walletClient.sendTransaction({
586
971
  account: walletClient.account,
587
- to,
588
- data: calldata,
972
+ to: createSpace.to,
973
+ data: createSpace.calldata,
589
974
  });
590
975
 
591
- await publicClient.waitForTransactionReceipt({ hash: createSpaceTxHash });
592
- }
976
+ await publicClient.waitForTransactionReceipt({ hash: createSpaceHash });
977
+
978
+ spaceIdHex = (await publicClient.readContract({
979
+ address: spaceRegistryAddress,
980
+ abi: SpaceRegistryAbi,
981
+ functionName: "addressToSpaceId",
982
+ args: [account.address],
983
+ })) as Hex;
984
+
985
+ const spaceId = spaceIdHex.slice(2, 34).toLowerCase();
986
+ const profileTx = await geo.personalSpaces.publishEdit({
987
+ name: "Create personal space profile",
988
+ spaceId,
989
+ author: spaceId,
990
+ ops: createSpace.ops,
991
+ });
593
992
 
594
- // Look up the space ID
595
- const spaceIdHex = (await publicClient.readContract({
596
- address: TESTNET.SPACE_REGISTRY_ADDRESS,
597
- abi: SpaceRegistryAbi,
598
- functionName: "addressToSpaceId",
599
- args: [account.address],
600
- })) as Hex;
993
+ const profileHash = await walletClient.sendTransaction({
994
+ account: walletClient.account,
995
+ to: profileTx.to,
996
+ data: profileTx.calldata,
997
+ });
998
+
999
+ await publicClient.waitForTransactionReceipt({ hash: profileHash });
1000
+
1001
+ const topicTx = geo.personalSpaces.setTopic({
1002
+ spaceId,
1003
+ topicId: createSpace.spaceEntityId,
1004
+ });
1005
+
1006
+ const topicHash = await walletClient.sendTransaction({
1007
+ account: walletClient.account,
1008
+ to: topicTx.to,
1009
+ data: topicTx.calldata,
1010
+ });
1011
+
1012
+ await publicClient.waitForTransactionReceipt({ hash: topicHash });
1013
+ }
601
1014
 
602
- // Convert bytes16 hex to UUID string (without dashes)
603
1015
  const spaceId = spaceIdHex.slice(2, 34).toLowerCase();
604
- console.log("spaceId", spaceId);
1016
+ console.log("Personal space ID", spaceId);
1017
+ ```
605
1018
 
606
- // Create an entity with some data
607
- const { ops, id: entityId } = Graph.createEntity({
608
- name: "Test Entity",
609
- description: "Created via SDK",
1019
+ ## Utilities
1020
+
1021
+ ### IDs
1022
+
1023
+ Entities, properties, relations, spaces, and proposals use globally unique IDs. The canonical SDK ID is a UUID v4 without dashes.
1024
+
1025
+ ```ts
1026
+ import { Id, IdUtils } from "@geoprotocol/geo-sdk";
1027
+
1028
+ const generatedId = IdUtils.generate();
1029
+ const checkedId = Id(generatedId);
1030
+ const bytes = IdUtils.toBytes(checkedId);
1031
+ ```
1032
+
1033
+ ### Positions
1034
+
1035
+ `Position` provides fractional indexing helpers for ordered relations and other ordered items.
1036
+
1037
+ ```ts
1038
+ import { Position } from "@geoprotocol/geo-sdk";
1039
+
1040
+ const first = Position.generate();
1041
+ const between = Position.generateBetween(first, null);
1042
+ const sorted = Position.sort([between, null, first]);
1043
+ ```
1044
+
1045
+ ### Wallet Helpers
1046
+
1047
+ The Geo Genesis browser uses a smart account associated with your account. You can use the same account from code by exporting your private key from https://www.geobrowser.io/export-wallet.
1048
+
1049
+ ```ts
1050
+ import {
1051
+ getSmartAccountWalletClient,
1052
+ getWalletClient,
1053
+ } from "@geoprotocol/geo-sdk";
1054
+
1055
+ const smartAccountWalletClient = await getSmartAccountWalletClient({
1056
+ privateKey,
610
1057
  });
611
- console.log("entityId", entityId);
612
1058
 
613
- // Publish to IPFS and get calldata for on-chain submission
614
- const { cid, editId, to, calldata } = await personalSpace.publishEdit({
615
- name: "Test Edit",
1059
+ const eoaWalletClient = await getWalletClient({
1060
+ privateKey,
1061
+ });
1062
+ ```
1063
+
1064
+ Be careful with private keys. Do not commit them to version control.
1065
+
1066
+ ## Legacy API
1067
+
1068
+ The legacy namespaces remain exported for compatibility, but new code should prefer `Ops` and `createGeoClient`.
1069
+
1070
+ | Legacy API | Preferred API |
1071
+ | -------------------------- | ------------------------------------------------------------------------------------ |
1072
+ | `Graph.createEntity(...)` | `Ops.entities.create(...)` |
1073
+ | `Graph.updateEntity(...)` | `Ops.entities.update(...)` |
1074
+ | `Graph.deleteEntity(...)` | `geo.entities.delete(...)` |
1075
+ | `Graph.createImage(...)` | `geo.images.create(...)` |
1076
+ | `Graph.createComment(...)` | `geo.comments.create(...)` or `Ops.comments.create(...)` with supplied reply context |
1077
+ | `Ipfs.publishEdit(...)` | `geo.personalSpaces.publishEdit(...)` or `geo.daoSpaces.proposeEdit(...)` |
1078
+ | `Ipfs.uploadImage(...)` | `geo.storage.uploadImage(...)` |
1079
+ | `Ipfs.uploadCSV(...)` | `geo.storage.uploadCSV(...)` |
1080
+ | `personalSpace.*` | `geo.personalSpaces.*` |
1081
+ | `daoSpace.*` | `geo.daoSpaces.*` |
1082
+ | root encoding helpers | configured client transaction helpers where available |
1083
+
1084
+ ### Legacy `Graph`
1085
+
1086
+ ```ts
1087
+ import { Graph } from "@geoprotocol/geo-sdk";
1088
+
1089
+ const { id: propertyId, ops: propertyOps } = Graph.createProperty({
1090
+ name: "Website",
1091
+ dataType: "TEXT",
1092
+ });
1093
+
1094
+ const { id: typeId, ops: typeOps } = Graph.createType({
1095
+ name: "Restaurant",
1096
+ properties: [propertyId],
1097
+ });
1098
+
1099
+ const { id: entityId, ops: entityOps } = Graph.createEntity({
1100
+ name: "Yum Yum",
1101
+ types: [typeId],
1102
+ values: [
1103
+ {
1104
+ property: propertyId,
1105
+ type: "text",
1106
+ value: "https://example.com",
1107
+ },
1108
+ ],
1109
+ });
1110
+
1111
+ const { ops: updateOps } = Graph.updateEntity({
1112
+ id: entityId,
1113
+ name: "Updated name",
1114
+ });
1115
+
1116
+ const { ops: deleteOps } = await Graph.deleteEntity({
1117
+ id: entityId,
616
1118
  spaceId,
1119
+ network: "TESTNET",
1120
+ });
1121
+ ```
1122
+
1123
+ Legacy relation helpers:
1124
+
1125
+ ```ts
1126
+ const { id: relationId, ops: createRelationOps } = Graph.createRelation({
1127
+ fromEntity: personId,
1128
+ toEntity: restaurantId,
1129
+ type: likesPropertyId,
1130
+ });
1131
+
1132
+ const { ops: updateRelationOps } = Graph.updateRelation({
1133
+ id: relationId,
1134
+ position,
1135
+ });
1136
+
1137
+ const { ops: deleteRelationOps } = Graph.deleteRelation({
1138
+ id: relationId,
1139
+ });
1140
+ ```
1141
+
1142
+ Legacy image and comment helpers:
1143
+
1144
+ ```ts
1145
+ const image = await Graph.createImage({
1146
+ url: "https://example.com/image.png",
1147
+ network: "TESTNET",
1148
+ });
1149
+
1150
+ const comment = await Graph.createComment({
1151
+ content: "Looks good.",
1152
+ replyTo: { entityId, spaceId },
1153
+ network: "TESTNET",
1154
+ });
1155
+ ```
1156
+
1157
+ ### Legacy `Ipfs`
1158
+
1159
+ ```ts
1160
+ import { Ipfs } from "@geoprotocol/geo-sdk";
1161
+
1162
+ const edit = await Ipfs.publishEdit({
1163
+ name: "Create entity",
1164
+ author: spaceId,
617
1165
  ops,
1166
+ network: "TESTNET",
1167
+ });
1168
+
1169
+ const image = await Ipfs.uploadImage(
1170
+ { url: "https://example.com/image.png" },
1171
+ "TESTNET",
1172
+ );
1173
+
1174
+ const csvCid = await Ipfs.uploadCSV("name,score\nAlice,10", "TESTNET");
1175
+ ```
1176
+
1177
+ ### Legacy `personalSpace`
1178
+
1179
+ ```ts
1180
+ import { personalSpace } from "@geoprotocol/geo-sdk";
1181
+
1182
+ const hasSpace = await personalSpace.hasSpace({
1183
+ address: account.address,
1184
+ });
1185
+
1186
+ const createSpace = personalSpace.createSpace();
1187
+
1188
+ const publish = await personalSpace.publishEdit({
1189
+ name: "My Edit",
1190
+ spaceId,
618
1191
  author: spaceId,
1192
+ ops,
1193
+ network: "TESTNET",
1194
+ });
1195
+ ```
1196
+
1197
+ ### Legacy `daoSpace`
1198
+
1199
+ ```ts
1200
+ import { daoSpace } from "@geoprotocol/geo-sdk";
1201
+
1202
+ const createDao = await daoSpace.createSpace({
1203
+ name: "Research DAO",
1204
+ author: authorSpaceId,
1205
+ initialEditorSpaceIds: [authorSpaceId],
1206
+ votingSettings,
1207
+ network: "TESTNET",
1208
+ });
1209
+
1210
+ const proposal = await daoSpace.proposeEdit({
1211
+ name: "Update entity",
1212
+ ops,
1213
+ author: authorSpaceId,
1214
+ daoSpaceAddress,
1215
+ callerSpaceId: authorSpaceId,
1216
+ daoSpaceId,
1217
+ network: "TESTNET",
1218
+ });
1219
+
1220
+ const vote = daoSpace.voteProposal({
1221
+ authorSpaceId,
1222
+ spaceId: daoSpaceId,
1223
+ proposalId,
1224
+ versionId: 1,
1225
+ vote: "YES",
619
1226
  network: "TESTNET",
620
1227
  });
621
- console.log("cid", cid);
622
- console.log("editId", editId);
623
1228
 
624
- // Submit the edit on-chain
625
- const publishTxHash = await walletClient.sendTransaction({
626
- account: walletClient.account,
627
- to,
628
- data: calldata,
1229
+ const execute = daoSpace.executeProposal({
1230
+ authorSpaceId,
1231
+ spaceId: daoSpaceId,
1232
+ proposalId,
1233
+ network: "TESTNET",
1234
+ });
1235
+ ```
1236
+
1237
+ Legacy DAO membership helpers:
1238
+
1239
+ ```ts
1240
+ daoSpace.proposeAddMember({
1241
+ authorSpaceId,
1242
+ spaceId: daoSpaceId,
1243
+ daoSpaceAddress,
1244
+ newMemberSpaceId,
1245
+ network: "TESTNET",
1246
+ });
1247
+
1248
+ daoSpace.proposeRemoveMember({
1249
+ authorSpaceId,
1250
+ spaceId: daoSpaceId,
1251
+ daoSpaceAddress,
1252
+ memberToRemoveSpaceId,
1253
+ network: "TESTNET",
1254
+ });
1255
+
1256
+ daoSpace.proposeAddEditor({
1257
+ authorSpaceId,
1258
+ spaceId: daoSpaceId,
1259
+ daoSpaceAddress,
1260
+ newEditorSpaceId,
1261
+ network: "TESTNET",
629
1262
  });
630
- console.log("publishTxHash", publishTxHash);
631
1263
 
632
- const publishReceipt = await publicClient.waitForTransactionReceipt({
633
- hash: publishTxHash,
1264
+ daoSpace.proposeRemoveEditor({
1265
+ authorSpaceId,
1266
+ spaceId: daoSpaceId,
1267
+ daoSpaceAddress,
1268
+ editorToRemoveSpaceId,
1269
+ network: "TESTNET",
634
1270
  });
635
- console.log("Successfully published edit to space", spaceId);
1271
+
1272
+ daoSpace.proposeUpdateVotingSettings({
1273
+ authorSpaceId,
1274
+ spaceId: daoSpaceId,
1275
+ daoSpaceAddress,
1276
+ votingSettings,
1277
+ network: "TESTNET",
1278
+ });
1279
+
1280
+ daoSpace.proposeRequestMembership({
1281
+ authorSpaceId: requesterSpaceId,
1282
+ spaceId: daoSpaceId,
1283
+ network: "TESTNET",
1284
+ });
1285
+ ```
1286
+
1287
+ ### Legacy Encoding Helpers
1288
+
1289
+ Low-level encoding helpers are still available for compatibility when you need exact calldata primitives:
1290
+
1291
+ ```ts
1292
+ import {
1293
+ getCreateDaoSpaceCalldata,
1294
+ getCreatePersonalSpaceCalldata,
1295
+ getProcessGeoProposalArguments,
1296
+ } from "@geoprotocol/geo-sdk";
1297
+
1298
+ const personalSpaceCalldata = getCreatePersonalSpaceCalldata();
1299
+
1300
+ const daoSpaceCalldata = getCreateDaoSpaceCalldata({
1301
+ votingSettings,
1302
+ initialEditorSpaceIds: [authorSpaceId],
1303
+ initialMemberSpaceIds: [],
1304
+ initialEditsContentUri:
1305
+ "ipfs://bafkreihdwdcefgh4dqkjv67uzcmw7ojee6xedzdetojuzjevtenxquvyku",
1306
+ });
1307
+
1308
+ const proposalArgs = getProcessGeoProposalArguments(
1309
+ spacePluginAddress,
1310
+ "ipfs://bafkreihdwdcefgh4dqkjv67uzcmw7ojee6xedzdetojuzjevtenxquvyku",
1311
+ );
636
1312
  ```