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