@arkiv-network/sdk 0.7.0-dev.1 → 0.8.0-advanced.2

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 (237) hide show
  1. package/README.md +71 -16
  2. package/dist/actions/advanced/index.cjs +13 -0
  3. package/dist/actions/advanced/index.d.cts +8 -0
  4. package/dist/actions/advanced/index.d.ts +9 -0
  5. package/dist/actions/advanced/index.js +9 -0
  6. package/dist/advanced-8ZPTmmaN.js +598 -0
  7. package/dist/advanced-8ZPTmmaN.js.map +1 -0
  8. package/dist/advanced-fPImL6lW.cjs +656 -0
  9. package/dist/arkivTransactions-D74gERHG.d.cts +571 -0
  10. package/dist/arkivTransactions-D74gERHG.d.cts.map +1 -0
  11. package/dist/arkivTransactions-NtGD6uk5.d.ts +571 -0
  12. package/dist/arkivTransactions-NtGD6uk5.d.ts.map +1 -0
  13. package/dist/attr/index.cjs +35 -0
  14. package/dist/attr/index.d.cts +2 -0
  15. package/dist/attr/index.d.ts +3 -0
  16. package/dist/attr/index.js +4 -0
  17. package/dist/attributes-BhY5DwJp.js +809 -0
  18. package/dist/attributes-BhY5DwJp.js.map +1 -0
  19. package/dist/attributes-Df9IsCVs.cjs +1029 -0
  20. package/dist/chains/index.cjs +2 -3
  21. package/dist/chains/index.d.cts +22 -60
  22. package/dist/chains/index.d.cts.map +1 -1
  23. package/dist/chains/index.d.ts +22 -60
  24. package/dist/chains/index.d.ts.map +1 -1
  25. package/dist/chains/index.js +2 -2
  26. package/dist/chains-BJrZs6sG.js +57 -0
  27. package/dist/chains-BJrZs6sG.js.map +1 -0
  28. package/dist/chains-BYRTTipa.cjs +73 -0
  29. package/dist/codec-Car3KkFF.d.ts +102 -0
  30. package/dist/codec-Car3KkFF.d.ts.map +1 -0
  31. package/dist/entity/index.cjs +20 -0
  32. package/dist/entity/index.d.cts +4 -0
  33. package/dist/entity/index.d.ts +5 -0
  34. package/dist/entity/index.js +7 -0
  35. package/dist/entity-B00Li04j.cjs +71 -0
  36. package/dist/entity-B7G8q_Eo.cjs +0 -0
  37. package/dist/entity-BYQR1uoe.js +67 -0
  38. package/dist/entity-BYQR1uoe.js.map +1 -0
  39. package/dist/entity-CD_qFF2M.js +0 -0
  40. package/dist/errors-BVbjKVMN.cjs +56 -0
  41. package/dist/errors-LTe7bOYS.js +33 -0
  42. package/dist/errors-LTe7bOYS.js.map +1 -0
  43. package/dist/expiry-BELY2Kpw.cjs +135 -0
  44. package/dist/expiry-BjpxZZQU.js +95 -0
  45. package/dist/expiry-BjpxZZQU.js.map +1 -0
  46. package/dist/expiry-DRV29F1r.d.cts +79 -0
  47. package/dist/expiry-DRV29F1r.d.cts.map +1 -0
  48. package/dist/expiry-nDbCgZX4.d.ts +79 -0
  49. package/dist/expiry-nDbCgZX4.d.ts.map +1 -0
  50. package/dist/flags-5dTNRnow.d.ts +41 -0
  51. package/dist/flags-5dTNRnow.d.ts.map +1 -0
  52. package/dist/flags-CRjPpoh3.d.cts +41 -0
  53. package/dist/flags-CRjPpoh3.d.cts.map +1 -0
  54. package/dist/flags-DFBs2J94.js +33 -0
  55. package/dist/flags-DFBs2J94.js.map +1 -0
  56. package/dist/flags-kr276q6i.cjs +43 -0
  57. package/dist/index-B5j76711.d.ts +184 -0
  58. package/dist/index-B5j76711.d.ts.map +1 -0
  59. package/dist/index-BF7Mq9Is.d.cts +280 -0
  60. package/dist/index-BF7Mq9Is.d.cts.map +1 -0
  61. package/dist/index-CA4V7Pdh.d.cts +167 -0
  62. package/dist/index-CA4V7Pdh.d.cts.map +1 -0
  63. package/dist/index-CV5zCdpa.d.cts +417 -0
  64. package/dist/index-CV5zCdpa.d.cts.map +1 -0
  65. package/dist/index-DHDud2hC.d.ts +280 -0
  66. package/dist/index-DHDud2hC.d.ts.map +1 -0
  67. package/dist/index-DvYvrkdk.d.ts +320 -0
  68. package/dist/index-DvYvrkdk.d.ts.map +1 -0
  69. package/dist/index-EeUO0nnJ.d.cts +184 -0
  70. package/dist/index-EeUO0nnJ.d.cts.map +1 -0
  71. package/dist/index-fX_XyqLp.d.ts +167 -0
  72. package/dist/index-fX_XyqLp.d.ts.map +1 -0
  73. package/dist/index.cjs +411 -405
  74. package/dist/index.d.cts +360 -209
  75. package/dist/index.d.cts.map +1 -1
  76. package/dist/index.d.ts +361 -209
  77. package/dist/index.d.ts.map +1 -1
  78. package/dist/index.js +342 -387
  79. package/dist/index.js.map +1 -1
  80. package/dist/key-D1Bh7MF0.js +70 -0
  81. package/dist/key-D1Bh7MF0.js.map +1 -0
  82. package/dist/key-WKhXnXuU.cjs +98 -0
  83. package/dist/logger-CVpvU8ik.js +153 -0
  84. package/dist/logger-CVpvU8ik.js.map +1 -0
  85. package/dist/logger-Cr15lPAQ.cjs +188 -0
  86. package/dist/payload-BNoud2KZ.d.cts +94 -0
  87. package/dist/payload-BNoud2KZ.d.cts.map +1 -0
  88. package/dist/payload-Bz6651ax.js +67 -0
  89. package/dist/payload-Bz6651ax.js.map +1 -0
  90. package/dist/payload-CcUj85ys.d.ts +94 -0
  91. package/dist/payload-CcUj85ys.d.ts.map +1 -0
  92. package/dist/payload-KC14SDlN.cjs +83 -0
  93. package/dist/query/index.cjs +26 -150
  94. package/dist/query/index.d.cts +5 -3
  95. package/dist/query/index.d.ts +6 -3
  96. package/dist/query/index.js +8 -138
  97. package/dist/queryBuilder-DM6AWCBa.js +820 -0
  98. package/dist/queryBuilder-DM6AWCBa.js.map +1 -0
  99. package/dist/queryBuilder-DbUMEZln.cjs +944 -0
  100. package/dist/selection-C4UhsMOO.d.ts +434 -0
  101. package/dist/selection-C4UhsMOO.d.ts.map +1 -0
  102. package/dist/selection-DPg0uyJf.d.cts +433 -0
  103. package/dist/selection-DPg0uyJf.d.cts.map +1 -0
  104. package/dist/types/index.cjs +3 -3
  105. package/dist/types/index.d.cts +7 -2
  106. package/dist/types/index.d.ts +8 -2
  107. package/dist/types/index.js +3 -3
  108. package/dist/types-Bu_ZwMbZ.js +0 -0
  109. package/dist/types-NRqymi6S.cjs +0 -0
  110. package/dist/utils/index.cjs +4 -17
  111. package/dist/utils/index.d.cts +3 -17
  112. package/dist/utils/index.d.ts +3 -17
  113. package/dist/utils/index.js +4 -18
  114. package/package.json +26 -5
  115. package/src/actions/advanced/advanced.test.ts +258 -0
  116. package/src/actions/advanced/buildMutation.ts +104 -0
  117. package/src/actions/advanced/getMutationResult.ts +112 -0
  118. package/src/actions/advanced/index.ts +21 -0
  119. package/src/actions/advanced/pingTransaction.ts +43 -0
  120. package/src/actions/advanced/sendMutation.ts +59 -0
  121. package/src/actions/public/getEntity.ts +27 -43
  122. package/src/actions/public/getEntityNonce.ts +30 -0
  123. package/src/actions/public/predictEntityKeys.test.ts +143 -0
  124. package/src/actions/public/predictEntityKeys.ts +138 -0
  125. package/src/actions/public/query.ts +35 -67
  126. package/src/actions/public/watchEntityEvents.test.ts +414 -0
  127. package/src/actions/public/watchEntityEvents.ts +225 -0
  128. package/src/actions/wallet/changeOwnership.ts +2 -2
  129. package/src/actions/wallet/createEntity.ts +99 -24
  130. package/src/actions/wallet/extendEntity.ts +37 -13
  131. package/src/actions/wallet/mutateEntities.test.ts +76 -0
  132. package/src/actions/wallet/mutateEntities.ts +16 -33
  133. package/src/actions/wallet/patchEntity.ts +90 -0
  134. package/src/attr/attributes.test.ts +120 -0
  135. package/src/attr/attributes.ts +190 -0
  136. package/src/attr/codec.test.ts +162 -0
  137. package/src/attr/codec.ts +220 -0
  138. package/src/attr/decimal.ts +86 -0
  139. package/src/attr/errors.ts +145 -0
  140. package/src/attr/index.ts +50 -0
  141. package/src/attr/names.ts +106 -0
  142. package/src/attr/types.ts +150 -0
  143. package/src/attr/values.test.ts +271 -0
  144. package/src/attr/values.ts +502 -0
  145. package/src/chains/cheesecake.ts +22 -0
  146. package/src/chains/index.ts +1 -2
  147. package/src/clients/createPublicClient.test.ts +3 -5
  148. package/src/clients/createPublicClient.ts +5 -4
  149. package/src/clients/createWalletClient.test.ts +2 -2
  150. package/src/clients/createWalletClient.ts +13 -7
  151. package/src/clients/decorators/arkivAdvanced.ts +144 -0
  152. package/src/clients/decorators/arkivPublic.ts +172 -139
  153. package/src/clients/decorators/arkivWallet.ts +101 -94
  154. package/src/entity/create.test.ts +184 -0
  155. package/src/entity/errors.ts +38 -0
  156. package/src/entity/events.ts +16 -0
  157. package/src/entity/expiry.test.ts +195 -0
  158. package/src/entity/expiry.ts +181 -0
  159. package/src/entity/flags.ts +65 -0
  160. package/src/entity/index.ts +15 -0
  161. package/src/entity/key.ts +68 -0
  162. package/src/entity/operations.ts +134 -0
  163. package/src/entity/params.ts +15 -0
  164. package/src/errors.ts +26 -49
  165. package/src/index.ts +19 -0
  166. package/src/query/engine.ts +68 -137
  167. package/src/query/errors.ts +151 -0
  168. package/src/query/expression.test.ts +286 -0
  169. package/src/query/expression.ts +493 -0
  170. package/src/query/index.ts +40 -4
  171. package/src/query/injection.test.ts +151 -0
  172. package/src/query/queryBuilder.test.ts +342 -637
  173. package/src/query/queryBuilder.ts +133 -359
  174. package/src/query/queryResult.ts +54 -51
  175. package/src/query/selection.test.ts +48 -98
  176. package/src/query/selection.ts +77 -65
  177. package/src/types/entity.test.ts +44 -155
  178. package/src/types/entity.ts +60 -52
  179. package/src/types/events.ts +59 -16
  180. package/src/types/index.ts +20 -17
  181. package/src/types/rpcSchema.ts +69 -37
  182. package/src/utils/arkivTransactions.test.ts +600 -155
  183. package/src/utils/arkivTransactions.ts +350 -219
  184. package/src/utils/chains.test.ts +21 -24
  185. package/src/utils/compression.ts +1 -1
  186. package/src/utils/entities.test.ts +62 -0
  187. package/src/utils/entities.ts +109 -37
  188. package/src/utils/expirationTime.ts +114 -10
  189. package/src/utils/logger.ts +2 -2
  190. package/src/utils/revert.test.ts +99 -0
  191. package/src/utils/revert.ts +145 -0
  192. package/dist/chains-BifaY04V.cjs +0 -108
  193. package/dist/chains-CrtkVbBh.js +0 -86
  194. package/dist/chains-CrtkVbBh.js.map +0 -1
  195. package/dist/consts-Bz0LZQZY.js +0 -7
  196. package/dist/consts-Bz0LZQZY.js.map +0 -1
  197. package/dist/consts-CxT1-RXU.cjs +0 -18
  198. package/dist/entity-BhSXaB23.cjs +0 -81
  199. package/dist/entity-CN41IOrq.d.cts +0 -401
  200. package/dist/entity-CN41IOrq.d.cts.map +0 -1
  201. package/dist/entity-DGfVyB-a.js +0 -71
  202. package/dist/entity-DGfVyB-a.js.map +0 -1
  203. package/dist/entity-NnEVlePN.d.ts +0 -401
  204. package/dist/entity-NnEVlePN.d.ts.map +0 -1
  205. package/dist/index-ClkpS6s_.d.ts +0 -481
  206. package/dist/index-ClkpS6s_.d.ts.map +0 -1
  207. package/dist/index-w4qop0Pe.d.cts +0 -481
  208. package/dist/index-w4qop0Pe.d.cts.map +0 -1
  209. package/dist/payload-BpNUPOnY.js +0 -13
  210. package/dist/payload-BpNUPOnY.js.map +0 -1
  211. package/dist/payload-C2KHOUoj.d.ts +0 -6
  212. package/dist/payload-C2KHOUoj.d.ts.map +0 -1
  213. package/dist/payload-CJN8fTvX.d.cts +0 -6
  214. package/dist/payload-CJN8fTvX.d.cts.map +0 -1
  215. package/dist/payload-D8YLKcJj.cjs +0 -23
  216. package/dist/query/index.js.map +0 -1
  217. package/dist/queryBuilder-BE_XhLLa.js +0 -735
  218. package/dist/queryBuilder-BE_XhLLa.js.map +0 -1
  219. package/dist/queryBuilder-J37buGCi.cjs +0 -865
  220. package/dist/utils/index.d.cts.map +0 -1
  221. package/dist/utils/index.d.ts.map +0 -1
  222. package/dist/utils/index.js.map +0 -1
  223. package/src/actions/public/buildQuery.ts +0 -10
  224. package/src/actions/public/subscribeEntityEvents.ts +0 -93
  225. package/src/actions/wallet/updateEntity.ts +0 -59
  226. package/src/chains/braga.ts +0 -26
  227. package/src/chains/kaolin.ts +0 -29
  228. package/src/query/engine.test.ts +0 -571
  229. package/src/query/predicate.test.ts +0 -461
  230. package/src/query/predicate.ts +0 -144
  231. package/src/query/queryResult.test.ts +0 -270
  232. package/src/query/selectQueryBuilder.test.ts +0 -213
  233. package/src/types/attributes.ts +0 -28
  234. package/src/utils/validation.test.ts +0 -106
  235. package/src/utils/validation.ts +0 -80
  236. /package/dist/{types-Ci4BRClw.js → attr-DlmErkvR.cjs} +0 -0
  237. /package/dist/{types-a8gm_IaQ.cjs → attr-GcQKatub.js} +0 -0
@@ -1,270 +0,0 @@
1
- import { beforeEach, describe, expect, jest, test } from "bun:test"
2
- import { NoCursorOrLimitError, NoMoreResultsError } from "../errors"
3
- import { Entity } from "../types/entity"
4
- import type { QueryBuilder } from "./queryBuilder"
5
- import { QueryResult } from "./queryResult"
6
-
7
- describe("QueryResult", () => {
8
- let mockQueryBuilder: QueryBuilder
9
- let mockEntities: Entity[]
10
-
11
- beforeEach(() => {
12
- // Create mock entities
13
- mockEntities = [
14
- new Entity("0x1" as const, "0xowner1" as const, 1000n, new Uint8Array([1]), []),
15
- new Entity("0x2" as const, "0xowner2" as const, 2000n, new Uint8Array([2]), []),
16
- new Entity("0x3" as const, "0xowner3" as const, 3000n, new Uint8Array([3]), []),
17
- ]
18
-
19
- // Create mock query builder
20
- mockQueryBuilder = {
21
- cursor: jest.fn().mockReturnThis(),
22
- fetch: jest.fn(),
23
- } as unknown as QueryBuilder
24
- })
25
-
26
- describe("constructor", () => {
27
- test("creates QueryResult with entities and query builder", () => {
28
- const result = new QueryResult(
29
- mockEntities,
30
- mockQueryBuilder,
31
- undefined,
32
- undefined,
33
- undefined,
34
- )
35
-
36
- expect(result.entities).toEqual(mockEntities)
37
- expect(result.entities).toHaveLength(3)
38
- })
39
-
40
- test("creates QueryResult with limit and cursor", () => {
41
- const result = new QueryResult(mockEntities, mockQueryBuilder, "0xABC123", 10, undefined)
42
-
43
- expect(result.entities).toEqual(mockEntities)
44
- })
45
-
46
- test("creates QueryResult with undefined limit and cursor", () => {
47
- const result = new QueryResult(
48
- mockEntities,
49
- mockQueryBuilder,
50
- undefined,
51
- undefined,
52
- undefined,
53
- )
54
-
55
- expect(result.entities).toEqual(mockEntities)
56
- })
57
-
58
- test("creates QueryResult with empty entities array", () => {
59
- const result = new QueryResult([], mockQueryBuilder, "0xABC123", 10, undefined)
60
-
61
- expect(result.entities).toEqual([])
62
- expect(result.entities).toHaveLength(0)
63
- })
64
- })
65
-
66
- describe("next()", () => {
67
- test("throws NoCursorOrLimitError when cursor is undefined", async () => {
68
- const result = new QueryResult(mockEntities, mockQueryBuilder, undefined, 10, undefined)
69
-
70
- await expect(result.next()).rejects.toThrow(NoCursorOrLimitError)
71
- await expect(result.next()).rejects.toThrow("Cursor and limit must be defined to fetch next")
72
- })
73
-
74
- test("throws NoCursorOrLimitError when limit is undefined", async () => {
75
- const result = new QueryResult(
76
- mockEntities,
77
- mockQueryBuilder,
78
- "0xABC123",
79
- undefined,
80
- undefined,
81
- )
82
-
83
- await expect(result.next()).rejects.toThrow(NoCursorOrLimitError)
84
- await expect(result.next()).rejects.toThrow("Cursor and limit must be defined to fetch next")
85
- })
86
-
87
- test("throws NoCursorOrLimitError when both cursor and limit are undefined", async () => {
88
- const result = new QueryResult(
89
- mockEntities,
90
- mockQueryBuilder,
91
- undefined,
92
- undefined,
93
- undefined,
94
- )
95
-
96
- await expect(result.next()).rejects.toThrow(NoCursorOrLimitError)
97
- })
98
-
99
- test("throws NoMoreResultsError when limit > entities.length", async () => {
100
- // limit is 4 but we have 3 entities, so limit > entities.length triggers error
101
- const result = new QueryResult(mockEntities, mockQueryBuilder, "0xABC123", 4, 123n)
102
-
103
- await expect(result.next()).rejects.toThrow(NoMoreResultsError)
104
- })
105
-
106
- test("fetches next page when limit equals entities length", async () => {
107
- const newEntities = [
108
- new Entity("0x4" as const, "0xowner4" as const, 4000n, new Uint8Array([4]), []),
109
- new Entity("0x5" as const, "0xowner5" as const, 5000n, new Uint8Array([5]), []),
110
- new Entity("0x6" as const, "0xowner6" as const, 6000n, new Uint8Array([6]), []),
111
- ]
112
-
113
- const mockFetch = jest.fn().mockResolvedValue({
114
- entities: newEntities,
115
- })
116
-
117
- mockQueryBuilder.fetch = mockFetch
118
- mockQueryBuilder.cursor = jest.fn().mockReturnValue(mockQueryBuilder)
119
-
120
- // limit is 3 and we have 3 entities, so there might be more
121
- const result = new QueryResult(mockEntities, mockQueryBuilder, "0xABC123", 3, undefined)
122
-
123
- await result.next()
124
-
125
- expect(mockQueryBuilder.cursor).toHaveBeenCalledWith("0xABC123")
126
- expect(mockFetch).toHaveBeenCalled()
127
- expect(result.entities).toEqual(newEntities)
128
- })
129
-
130
- test("updates offset correctly for multiple next calls", async () => {
131
- const initialEntities = [
132
- new Entity("0x1" as const, "0xowner1" as const, 1000n, new Uint8Array([1]), []),
133
- new Entity("0x2" as const, "0xowner2" as const, 2000n, new Uint8Array([2]), []),
134
- new Entity("0x3" as const, "0xowner3" as const, 3000n, new Uint8Array([3]), []),
135
- ]
136
-
137
- const newEntities1 = [
138
- new Entity("0x4" as const, "0xowner4" as const, 4000n, new Uint8Array([4]), []),
139
- new Entity("0x5" as const, "0xowner5" as const, 5000n, new Uint8Array([5]), []),
140
- new Entity("0x6" as const, "0xowner6" as const, 6000n, new Uint8Array([6]), []),
141
- ]
142
-
143
- const newEntities2 = [
144
- new Entity("0x7" as const, "0xowner7" as const, 7000n, new Uint8Array([7]), []),
145
- new Entity("0x8" as const, "0xowner8" as const, 8000n, new Uint8Array([8]), []),
146
- new Entity("0x9" as const, "0xowner9" as const, 9000n, new Uint8Array([9]), []),
147
- ]
148
-
149
- const mockCursor = jest.fn().mockReturnValue(mockQueryBuilder)
150
- const mockFetch = jest
151
- .fn()
152
- .mockResolvedValueOnce({
153
- entities: newEntities1,
154
- queryBuilder: mockQueryBuilder,
155
- cursor: "0xABC123",
156
- })
157
- .mockResolvedValueOnce({
158
- entities: newEntities2,
159
- queryBuilder: mockQueryBuilder,
160
- cursor: "0xABC123",
161
- })
162
-
163
- mockQueryBuilder.cursor = mockCursor
164
- mockQueryBuilder.fetch = mockFetch
165
-
166
- // limit = 3, entities.length = 3, so 3 < 3 is false (won't throw)
167
- const result = new QueryResult(initialEntities, mockQueryBuilder, "0xABC123", 3, undefined)
168
-
169
- // First next call - offset should be 0 + 3 = 3
170
- await result.next()
171
- expect(mockCursor).toHaveBeenCalledWith("0xABC123")
172
- expect(result.entities).toEqual(newEntities1)
173
-
174
- // Second next call - offset should be 3 + 3 = 6
175
- await result.next()
176
- expect(mockCursor).toHaveBeenCalledWith("0xABC123")
177
- expect(result.entities).toEqual(newEntities2)
178
- })
179
-
180
- test("fetches next page when limit is greater than entities length", async () => {
181
- // limit is 5 but only 3 entities (5 > 3) so there is no more results
182
- // so NoMoreResultsError is thrown
183
- const result = new QueryResult(mockEntities, mockQueryBuilder, "0xABC123", 5, undefined)
184
-
185
- await expect(result.next()).rejects.toThrow(NoMoreResultsError)
186
- })
187
- })
188
-
189
- describe("pagination flow", () => {
190
- test("simulates complete pagination: next -> next -> previous -> previous", async () => {
191
- const page1 = [
192
- new Entity("0x1" as const, "0xowner1" as const, 1000n, new Uint8Array([1]), []),
193
- ]
194
- const page2 = [
195
- new Entity("0x2" as const, "0xowner2" as const, 2000n, new Uint8Array([2]), []),
196
- ]
197
- const page3 = [
198
- new Entity("0x3" as const, "0xowner3" as const, 3000n, new Uint8Array([3]), []),
199
- ]
200
-
201
- const mockCursor = jest.fn().mockReturnValue(mockQueryBuilder)
202
- const mockFetch = jest
203
- .fn()
204
- .mockResolvedValueOnce({
205
- entities: page2,
206
- queryBuilder: mockQueryBuilder,
207
- cursor: "0xABC123",
208
- }) // first next
209
- .mockResolvedValueOnce({
210
- entities: page3,
211
- queryBuilder: mockQueryBuilder,
212
- cursor: "0xABC123",
213
- }) // second next
214
- .mockResolvedValueOnce({
215
- entities: page2,
216
- queryBuilder: mockQueryBuilder,
217
- cursor: "0xABC123",
218
- }) // first previous
219
- .mockResolvedValueOnce({
220
- entities: page1,
221
- queryBuilder: mockQueryBuilder,
222
- cursor: "0xABC123",
223
- }) // second previous
224
-
225
- mockQueryBuilder.cursor = mockCursor
226
- mockQueryBuilder.fetch = mockFetch
227
-
228
- const result = new QueryResult(page1, mockQueryBuilder, "0xABC123", 1, undefined)
229
-
230
- // Start at page 1 (offset 0)
231
- expect(result.entities).toEqual(page1)
232
-
233
- // Next to page 2 (offset 1)
234
- await result.next()
235
- expect(mockCursor).toHaveBeenCalledWith("0xABC123")
236
- expect(result.entities).toEqual(page2)
237
-
238
- // Next to page 3 (offset 2)
239
- await result.next()
240
- expect(mockCursor).toHaveBeenCalledWith("0xABC123")
241
- expect(result.entities).toEqual(page3)
242
- })
243
-
244
- test("There is another page to fetch when limit < entities.length", async () => {
245
- // limit is 2 but 3 entities, so 2 < 3 there is another page to fetch
246
- const mockCursor = jest.fn().mockReturnValue(mockQueryBuilder)
247
- const mockFetch = jest
248
- .fn()
249
- .mockResolvedValueOnce({
250
- entities: mockEntities,
251
- queryBuilder: mockQueryBuilder,
252
- cursor: "0xABC123",
253
- })
254
- .mockResolvedValueOnce({
255
- entities: mockEntities,
256
- queryBuilder: mockQueryBuilder,
257
- cursor: "0xABC123",
258
- })
259
-
260
- mockQueryBuilder.cursor = mockCursor
261
- mockQueryBuilder.fetch = mockFetch
262
-
263
- const result = new QueryResult(mockEntities, mockQueryBuilder, "0xABC123", 2, undefined)
264
-
265
- await result.next()
266
- expect(mockCursor).toHaveBeenCalledWith("0xABC123")
267
- expect(result.entities).toEqual(mockEntities)
268
- })
269
- })
270
- })
@@ -1,213 +0,0 @@
1
- import { afterEach, beforeEach, describe, expect, jest, test } from "bun:test"
2
- import type { ArkivClient } from "../clients/baseClient"
3
- import type { Entity } from "../types/entity"
4
- import { RpcAttributeValueType } from "../types/rpcSchema"
5
- import * as entitiesUtils from "../utils/entities"
6
- import * as engine from "./engine"
7
- import { eq, gte } from "./predicate"
8
- import { SelectQueryBuilder } from "./queryBuilder"
9
- import { selectionToIncludeData } from "./selection"
10
-
11
- describe("SelectQueryBuilder", () => {
12
- let mockClient: ArkivClient
13
- let mockProcessQuery: any
14
-
15
- beforeEach(() => {
16
- mockClient = {
17
- request: jest.fn(),
18
- } as unknown as ArkivClient
19
-
20
- mockProcessQuery = jest.spyOn(engine, "processQuery")
21
- jest.spyOn(entitiesUtils, "entityFromRpcResult")
22
- })
23
-
24
- afterEach(() => {
25
- jest.restoreAllMocks()
26
- })
27
-
28
- test("select() with no argument forwards include-data for everything", async () => {
29
- mockProcessQuery.mockResolvedValue({ data: [] })
30
-
31
- await new SelectQueryBuilder(mockClient).where(eq("name", "test")).fetch()
32
-
33
- expect(mockProcessQuery).toHaveBeenCalledWith(
34
- mockClient,
35
- expect.objectContaining({
36
- predicates: [eq("name", "test")],
37
- includeData: selectionToIncludeData(),
38
- }),
39
- )
40
- })
41
-
42
- test('select("*") forwards include-data for everything', async () => {
43
- mockProcessQuery.mockResolvedValue({ data: [] })
44
-
45
- await new SelectQueryBuilder(mockClient, "*").fetch()
46
-
47
- expect(mockProcessQuery).toHaveBeenCalledWith(
48
- mockClient,
49
- expect.objectContaining({
50
- includeData: selectionToIncludeData("*"),
51
- }),
52
- )
53
- })
54
-
55
- test("select({ owner: true, attributes: true }) forwards the resolved include-data", async () => {
56
- mockProcessQuery.mockResolvedValue({ data: [] })
57
-
58
- await new SelectQueryBuilder(mockClient, { owner: true, attributes: true }).fetch()
59
-
60
- expect(mockProcessQuery).toHaveBeenCalledWith(
61
- mockClient,
62
- expect.objectContaining({
63
- includeData: selectionToIncludeData({ owner: true, attributes: true }),
64
- }),
65
- )
66
- })
67
-
68
- test("select({ owner: true }) forwards narrowed include-data", async () => {
69
- mockProcessQuery.mockResolvedValue({ data: [] })
70
-
71
- await new SelectQueryBuilder(mockClient, { owner: true }).fetch()
72
-
73
- const call = mockProcessQuery.mock.calls[0][1]
74
- expect(call.includeData).toEqual(selectionToIncludeData({ owner: true }))
75
- expect(call.includeData.owner).toBe(true)
76
- expect(call.includeData.creator).toBe(false)
77
- // key is opt-in: not selected here, so it is excluded
78
- expect(call.includeData.key).toBe(false)
79
- })
80
-
81
- test("does not forward the with* boolean flags", async () => {
82
- mockProcessQuery.mockResolvedValue({ data: [] })
83
-
84
- await new SelectQueryBuilder(mockClient, { attributes: true }).fetch()
85
-
86
- const call = mockProcessQuery.mock.calls[0][1]
87
- expect(call.withAttributes).toBeUndefined()
88
- expect(call.withMetadata).toBeUndefined()
89
- expect(call.withPayload).toBeUndefined()
90
- })
91
-
92
- test("shares the filtering/ordering/pagination chain methods", async () => {
93
- mockProcessQuery.mockResolvedValue({ data: [] })
94
-
95
- const owner = "0x1234567890123456789012345678901234567890" as const
96
- await new SelectQueryBuilder(mockClient, { attributes: true })
97
- .where(eq("name", "test"))
98
- .where(gte("age", 18))
99
- .ownedBy(owner)
100
- .createdBy(owner)
101
- .orderBy("name", "string", "desc")
102
- .limit(10)
103
- .cursor("0xABC123")
104
- .validAtBlock(123n)
105
- .fetch()
106
-
107
- expect(mockProcessQuery).toHaveBeenCalledWith(mockClient, {
108
- predicates: [eq("name", "test"), gte("age", 18)],
109
- limit: 10,
110
- cursor: "0xABC123",
111
- ownedBy: owner,
112
- createdBy: owner,
113
- orderBy: [{ name: "name", type: "string", desc: true }],
114
- validAtBlock: 123n,
115
- includeData: selectionToIncludeData({ attributes: true }),
116
- })
117
- })
118
-
119
- test("count() ignores the selection and requests no data", async () => {
120
- mockProcessQuery.mockResolvedValue({
121
- data: [
122
- { key: "0xabc", value: "data1" },
123
- { key: "0xdef", value: "data2" },
124
- ],
125
- })
126
-
127
- const count = await new SelectQueryBuilder(mockClient, "*")
128
- .where(eq("status", "active"))
129
- .count()
130
-
131
- expect(count).toBe(2)
132
- expect(mockProcessQuery).toHaveBeenCalledWith(mockClient, {
133
- predicates: [eq("status", "active")],
134
- limit: undefined,
135
- cursor: undefined,
136
- ownedBy: undefined,
137
- createdBy: undefined,
138
- orderBy: undefined,
139
- validAtBlock: undefined,
140
- withAttributes: false,
141
- withMetadata: false,
142
- withPayload: false,
143
- })
144
- })
145
-
146
- describe("result entities", () => {
147
- test("fetch returns flat Entity instances with the selected data and methods", async () => {
148
- mockProcessQuery.mockResolvedValue({
149
- data: [
150
- {
151
- key: "0xabc",
152
- owner: "0xowner",
153
- value: "0x68656c6c6f", // "hello"
154
- attributes: [
155
- { key: "category", value: "docs", valueType: RpcAttributeValueType.String },
156
- { key: "score", value: "0x2a", valueType: RpcAttributeValueType.Uint },
157
- ],
158
- },
159
- ],
160
- cursor: "",
161
- blockNumber: "0x1",
162
- })
163
-
164
- const result = await new SelectQueryBuilder(mockClient, {
165
- key: true,
166
- owner: true,
167
- attributes: true,
168
- payload: true,
169
- }).fetch()
170
-
171
- expect(result.entities).toHaveLength(1)
172
- const entity = result.entities[0] as unknown as Entity
173
- // flat, backwards-compatible field access
174
- expect(entity.key).toBe("0xabc")
175
- expect(entity.owner).toBe("0xowner")
176
- expect(entity.attributes).toEqual([
177
- { key: "category", value: "docs" },
178
- { key: "score", value: 42 },
179
- ])
180
- // entity methods are preserved on the result
181
- expect(typeof entity.toText).toBe("function")
182
- expect(entity.toText()).toBe("hello")
183
- })
184
-
185
- test("toJson decodes a JSON payload", async () => {
186
- mockProcessQuery.mockResolvedValue({
187
- data: [{ key: "0xabc", value: "0x7b2261223a317d" }], // {"a":1}
188
- cursor: "",
189
- blockNumber: "0x1",
190
- })
191
-
192
- const result = await new SelectQueryBuilder(mockClient, { payload: true }).fetch()
193
- const entity = result.entities[0] as unknown as Entity
194
- expect(entity.toJson()).toEqual({ a: 1 })
195
- })
196
-
197
- test("fields the RPC omits (because they were not selected) are undefined at runtime", async () => {
198
- // The RPC honours includeData and returns only the key.
199
- mockProcessQuery.mockResolvedValue({
200
- data: [{ key: "0xabc" }],
201
- cursor: "",
202
- blockNumber: "0x1",
203
- })
204
-
205
- const result = await new SelectQueryBuilder(mockClient, { key: true }).fetch()
206
- const entity = result.entities[0] as unknown as Entity
207
- expect(entity.key).toBe("0xabc")
208
- expect(entity.owner).toBeUndefined()
209
- expect(entity.payload).toBeUndefined()
210
- expect(entity.attributes).toEqual([])
211
- })
212
- })
213
- })
@@ -1,28 +0,0 @@
1
- import type { Hex } from "viem"
2
-
3
- /**
4
- * An entity attribute (key/value pair).
5
- *
6
- * Arkiv supports string, Hex and number values.
7
- *
8
- * Hex values are stored as the on-chain `EntityKey` type **only when they are
9
- * exactly 32 bytes** (66 characters, e.g. an entity key). Hex-looking strings
10
- * of any other length are stored as plain strings: they round-trip unchanged
11
- * and compare lexicographically, not as hex.
12
- *
13
- * Numeric values **must be non-negative integers** (they are stored on-chain
14
- * as unsigned 256-bit) — passing a non-integer (e.g. `1.5`) or negative number
15
- * throws an {@link InvalidAttributeError} at mutation time. To store a
16
- * non-integer value:
17
- * - Scale it to an integer (e.g. `1.5` -> `1500`, dividing by the same factor on
18
- * read) to keep numeric ordering and range queries working.
19
- * - Or pass it as a string (e.g. `"1.5"`), which sorts lexicographically and so
20
- * does not support numeric comparisons.
21
- *
22
- * To store a negative value, offset it into the non-negative range (subtracting
23
- * the offset on read) or store it as a string.
24
- */
25
- export type Attribute = {
26
- key: string
27
- value: number | string | Hex
28
- }
@@ -1,106 +0,0 @@
1
- import { describe, expect, it } from "bun:test"
2
- import { BLOCK_TIME } from "../consts"
3
- import { InvalidAttributeError, InvalidAttributeKeyError, InvalidExpirationError } from "../errors"
4
- import { validateAttribute, validateAttributeKey, validateExpiresIn } from "./validation"
5
-
6
- describe("validateExpiresIn", () => {
7
- it("accepts positive integers that are a multiple of the block time", () => {
8
- expect(() => validateExpiresIn(BLOCK_TIME)).not.toThrow()
9
- expect(() => validateExpiresIn(1000)).not.toThrow()
10
- expect(() => validateExpiresIn(BLOCK_TIME * 12345)).not.toThrow()
11
- })
12
-
13
- it("throws for non-integer values", () => {
14
- expect(() => validateExpiresIn(51.5)).toThrow(InvalidExpirationError)
15
- expect(() => validateExpiresIn(1.5)).toThrow(InvalidExpirationError)
16
- })
17
-
18
- it("throws for values that are not a multiple of the block time", () => {
19
- expect(() => validateExpiresIn(51)).toThrow(InvalidExpirationError)
20
- expect(() => validateExpiresIn(999)).toThrow(InvalidExpirationError)
21
- expect(() => validateExpiresIn(1)).toThrow(InvalidExpirationError)
22
- })
23
-
24
- it("throws for zero and negative values", () => {
25
- expect(() => validateExpiresIn(0)).toThrow(InvalidExpirationError)
26
- expect(() => validateExpiresIn(-1000)).toThrow(InvalidExpirationError)
27
- })
28
-
29
- it("throws for NaN and non-finite values", () => {
30
- expect(() => validateExpiresIn(Number.NaN)).toThrow(InvalidExpirationError)
31
- expect(() => validateExpiresIn(Number.POSITIVE_INFINITY)).toThrow(InvalidExpirationError)
32
- })
33
-
34
- it("includes the offending value in the error message", () => {
35
- expect(() => validateExpiresIn(51)).toThrow(/51/)
36
- })
37
- })
38
-
39
- describe("validateAttribute", () => {
40
- it("accepts string values", () => {
41
- expect(() => validateAttribute({ key: "k", value: "1.5" })).not.toThrow()
42
- expect(() => validateAttribute({ key: "k", value: "anything" })).not.toThrow()
43
- })
44
-
45
- it("accepts non-negative integer numeric values", () => {
46
- expect(() => validateAttribute({ key: "k", value: 0 })).not.toThrow()
47
- expect(() => validateAttribute({ key: "k", value: 123 })).not.toThrow()
48
- })
49
-
50
- it("throws for non-integer numeric values", () => {
51
- expect(() => validateAttribute({ key: "k", value: 1.5 })).toThrow(InvalidAttributeError)
52
- expect(() => validateAttribute({ key: "k", value: Number.NaN })).toThrow(InvalidAttributeError)
53
- })
54
-
55
- it("throws for negative numeric values", () => {
56
- expect(() => validateAttribute({ key: "k", value: -5 })).toThrow(InvalidAttributeError)
57
- expect(() => validateAttribute({ key: "k", value: -0.5 })).toThrow(InvalidAttributeError)
58
- })
59
-
60
- it("includes the attribute key and value in the error message", () => {
61
- expect(() => validateAttribute({ key: "my_key", value: 1.5 })).toThrow(/my_key/)
62
- expect(() => validateAttribute({ key: "my_key", value: 1.5 })).toThrow(/1\.5/)
63
- })
64
- })
65
-
66
- describe("validateAttributeKey", () => {
67
- it("accepts valid Ident32 keys", () => {
68
- expect(() => validateAttributeKey("a")).not.toThrow()
69
- expect(() => validateAttributeKey("tag")).not.toThrow()
70
- expect(() => validateAttributeKey("num_attr")).not.toThrow()
71
- expect(() => validateAttributeKey("a.b-c_d9")).not.toThrow()
72
- expect(() => validateAttributeKey("a".repeat(32))).not.toThrow()
73
- })
74
-
75
- it("throws for an empty key", () => {
76
- expect(() => validateAttributeKey("")).toThrow(InvalidAttributeKeyError)
77
- })
78
-
79
- it("throws when the key does not start with a lowercase letter", () => {
80
- expect(() => validateAttributeKey("Tag")).toThrow(InvalidAttributeKeyError)
81
- expect(() => validateAttributeKey("1tag")).toThrow(InvalidAttributeKeyError)
82
- expect(() => validateAttributeKey("_tag")).toThrow(InvalidAttributeKeyError)
83
- expect(() => validateAttributeKey(".tag")).toThrow(InvalidAttributeKeyError)
84
- })
85
-
86
- it("throws for invalid characters after the first", () => {
87
- expect(() => validateAttributeKey("strAttr")).toThrow(InvalidAttributeKeyError)
88
- expect(() => validateAttributeKey("has space")).toThrow(InvalidAttributeKeyError)
89
- expect(() => validateAttributeKey("has:colon")).toThrow(InvalidAttributeKeyError)
90
- expect(() => validateAttributeKey("héllo")).toThrow(InvalidAttributeKeyError)
91
- })
92
-
93
- it("throws for keys longer than 32 bytes", () => {
94
- expect(() => validateAttributeKey("a".repeat(33))).toThrow(InvalidAttributeKeyError)
95
- })
96
-
97
- it("names the offending character and position", () => {
98
- expect(() => validateAttributeKey("strAttr")).toThrow(/"A" at position 3/)
99
- })
100
-
101
- it("is applied by validateAttribute", () => {
102
- expect(() => validateAttribute({ key: "strAttr", value: "x" })).toThrow(
103
- InvalidAttributeKeyError,
104
- )
105
- })
106
- })