@arkiv-network/sdk 0.7.0 → 0.8.0-dev.1
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.
- package/README.md +29 -16
- package/dist/attr/index.cjs +34 -0
- package/dist/attr/index.d.cts +2 -0
- package/dist/attr/index.d.ts +2 -0
- package/dist/attr/index.js +3 -0
- package/dist/attr-BDZ-APg5.js +809 -0
- package/dist/attr-BDZ-APg5.js.map +1 -0
- package/dist/attr-BvxHN99W.cjs +1029 -0
- package/dist/chains/index.cjs +2 -3
- package/dist/chains/index.d.cts +39 -77
- package/dist/chains/index.d.cts.map +1 -1
- package/dist/chains/index.d.ts +22 -60
- package/dist/chains/index.d.ts.map +1 -1
- package/dist/chains/index.js +2 -2
- package/dist/chains-BJrZs6sG.js +57 -0
- package/dist/chains-BJrZs6sG.js.map +1 -0
- package/dist/chains-BYRTTipa.cjs +73 -0
- package/dist/changeOwnership-B7bEgla4.d.cts +509 -0
- package/dist/changeOwnership-B7bEgla4.d.cts.map +1 -0
- package/dist/changeOwnership-DwgtdSFc.d.ts +509 -0
- package/dist/changeOwnership-DwgtdSFc.d.ts.map +1 -0
- package/dist/entity/index.cjs +19 -0
- package/dist/entity/index.d.cts +4 -0
- package/dist/entity/index.d.ts +4 -0
- package/dist/entity/index.js +6 -0
- package/dist/entity-B0t6sN7F.cjs +71 -0
- package/dist/entity-BIp_df9u.js +67 -0
- package/dist/entity-BIp_df9u.js.map +1 -0
- package/dist/entity-CFhbAv0R.js +70 -0
- package/dist/entity-CFhbAv0R.js.map +1 -0
- package/dist/entity-wfBnZd91.cjs +98 -0
- package/dist/errors-D-eJkGVc.cjs +56 -0
- package/dist/errors-DGB8AgT2.js +33 -0
- package/dist/errors-DGB8AgT2.js.map +1 -0
- package/dist/expiry-BUYIXTGb.cjs +135 -0
- package/dist/expiry-BxOlvVWC.d.ts +79 -0
- package/dist/expiry-BxOlvVWC.d.ts.map +1 -0
- package/dist/expiry-DGRzrSan.js +95 -0
- package/dist/expiry-DGRzrSan.js.map +1 -0
- package/dist/expiry-DRV29F1r.d.cts +79 -0
- package/dist/expiry-DRV29F1r.d.cts.map +1 -0
- package/dist/flags-0Vl5rrHq.d.cts +41 -0
- package/dist/flags-0Vl5rrHq.d.cts.map +1 -0
- package/dist/flags-Ct8k0gpB.cjs +43 -0
- package/dist/flags-ZtlvOIIk.js +33 -0
- package/dist/flags-ZtlvOIIk.js.map +1 -0
- package/dist/flags-wmcMoAnN.d.ts +41 -0
- package/dist/flags-wmcMoAnN.d.ts.map +1 -0
- package/dist/index-Cgw1u5aY.d.cts +278 -0
- package/dist/index-Cgw1u5aY.d.cts.map +1 -0
- package/dist/index-ClgIuxJ8.d.ts +159 -0
- package/dist/index-ClgIuxJ8.d.ts.map +1 -0
- package/dist/index-D0CmtiWT.d.cts +159 -0
- package/dist/index-D0CmtiWT.d.cts.map +1 -0
- package/dist/index-D8LZt-3J.d.cts +417 -0
- package/dist/index-D8LZt-3J.d.cts.map +1 -0
- package/dist/index-DVYFaXNV.d.ts +417 -0
- package/dist/index-DVYFaXNV.d.ts.map +1 -0
- package/dist/index-VCqgn29D.d.ts +278 -0
- package/dist/index-VCqgn29D.d.ts.map +1 -0
- package/dist/index.cjs +675 -357
- package/dist/index.d.cts +251 -203
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +251 -203
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +626 -355
- package/dist/index.js.map +1 -1
- package/dist/payload-Bnw1hrvb.cjs +83 -0
- package/dist/payload-CIxSn_DW.js +67 -0
- package/dist/payload-CIxSn_DW.js.map +1 -0
- package/dist/payload-Dgx3PQeK.d.ts +94 -0
- package/dist/payload-Dgx3PQeK.d.ts.map +1 -0
- package/dist/payload-P0nWyjLg.d.cts +94 -0
- package/dist/payload-P0nWyjLg.d.cts.map +1 -0
- package/dist/query/index.cjs +24 -149
- package/dist/query/index.d.cts +5 -3
- package/dist/query/index.d.ts +5 -3
- package/dist/query/index.js +6 -137
- package/dist/queryBuilder-BXDvX1K_.cjs +1123 -0
- package/dist/queryBuilder-CbLL4pG5.js +963 -0
- package/dist/queryBuilder-CbLL4pG5.js.map +1 -0
- package/dist/selection-BvK6yX0g.d.cts +433 -0
- package/dist/selection-BvK6yX0g.d.cts.map +1 -0
- package/dist/selection-fpmI3ADQ.d.ts +433 -0
- package/dist/selection-fpmI3ADQ.d.ts.map +1 -0
- package/dist/types/index.cjs +4 -1
- package/dist/types/index.d.cts +7 -2
- package/dist/types/index.d.ts +7 -2
- package/dist/types/index.js +4 -1
- package/dist/utils/index.cjs +4 -17
- package/dist/utils/index.d.cts +3 -17
- package/dist/utils/index.d.ts +3 -17
- package/dist/utils/index.js +4 -18
- package/package.json +26 -5
- package/src/actions/public/getEntity.ts +27 -43
- package/src/actions/public/getEntityNonce.ts +30 -0
- package/src/actions/public/predictEntityKeys.test.ts +143 -0
- package/src/actions/public/predictEntityKeys.ts +138 -0
- package/src/actions/public/query.ts +33 -69
- package/src/actions/public/watchEntityEvents.test.ts +414 -0
- package/src/actions/public/watchEntityEvents.ts +225 -0
- package/src/actions/wallet/changeOwnership.ts +8 -8
- package/src/actions/wallet/createEntity.ts +104 -26
- package/src/actions/wallet/deleteEntity.ts +2 -3
- package/src/actions/wallet/extendEntity.ts +39 -16
- package/src/actions/wallet/mutateEntities.test.ts +76 -0
- package/src/actions/wallet/mutateEntities.ts +29 -74
- package/src/actions/wallet/patchEntity.ts +90 -0
- package/src/attr/attributes.test.ts +120 -0
- package/src/attr/attributes.ts +190 -0
- package/src/attr/codec.test.ts +162 -0
- package/src/attr/codec.ts +220 -0
- package/src/attr/decimal.ts +86 -0
- package/src/attr/errors.ts +145 -0
- package/src/attr/index.ts +50 -0
- package/src/attr/names.ts +106 -0
- package/src/attr/types.ts +150 -0
- package/src/attr/values.test.ts +271 -0
- package/src/attr/values.ts +502 -0
- package/src/chains/cheesecake.ts +22 -0
- package/src/chains/index.ts +1 -2
- package/src/clients/createPublicClient.test.ts +3 -5
- package/src/clients/createPublicClient.ts +5 -4
- package/src/clients/createWalletClient.test.ts +2 -2
- package/src/clients/createWalletClient.ts +13 -7
- package/src/clients/decorators/arkivPublic.ts +160 -139
- package/src/clients/decorators/arkivWallet.ts +91 -95
- package/src/consts.ts +2 -1
- package/src/entity/create.test.ts +184 -0
- package/src/entity/errors.ts +38 -0
- package/src/entity/events.ts +16 -0
- package/src/entity/expiry.test.ts +195 -0
- package/src/entity/expiry.ts +181 -0
- package/src/entity/flags.ts +65 -0
- package/src/entity/index.ts +14 -0
- package/src/entity/key.ts +68 -0
- package/src/entity/operations.ts +134 -0
- package/src/entity/params.ts +15 -0
- package/src/errors.ts +33 -28
- package/src/index.ts +6 -0
- package/src/query/engine.ts +59 -134
- package/src/query/errors.ts +151 -0
- package/src/query/expression.test.ts +286 -0
- package/src/query/expression.ts +493 -0
- package/src/query/index.ts +40 -4
- package/src/query/injection.test.ts +151 -0
- package/src/query/queryBuilder.test.ts +322 -637
- package/src/query/queryBuilder.ts +126 -359
- package/src/query/queryResult.ts +54 -51
- package/src/query/selection.test.ts +48 -98
- package/src/query/selection.ts +77 -65
- package/src/types/entity.test.ts +44 -155
- package/src/types/entity.ts +62 -44
- package/src/types/events.ts +64 -23
- package/src/types/index.ts +20 -16
- package/src/types/rpcSchema.ts +70 -33
- package/src/utils/arkivTransactions.test.ts +681 -38
- package/src/utils/arkivTransactions.ts +249 -104
- package/src/utils/chains.test.ts +21 -24
- package/src/utils/compression.ts +1 -1
- package/src/utils/entities.test.ts +62 -0
- package/src/utils/entities.ts +111 -29
- package/src/utils/expirationTime.ts +114 -10
- package/src/utils/logger.ts +2 -2
- package/src/utils/payload.ts +4 -2
- package/src/utils/revert.test.ts +99 -0
- package/src/utils/revert.ts +145 -0
- package/dist/chains-BifaY04V.cjs +0 -108
- package/dist/chains-CrtkVbBh.js +0 -86
- package/dist/chains-CrtkVbBh.js.map +0 -1
- package/dist/consts-AUoL-v42.cjs +0 -18
- package/dist/consts-BV4PR-de.js +0 -7
- package/dist/consts-BV4PR-de.js.map +0 -1
- package/dist/entity-B6g_EPm1.d.cts +0 -382
- package/dist/entity-B6g_EPm1.d.cts.map +0 -1
- package/dist/entity-qoLMAuuk.d.ts +0 -382
- package/dist/entity-qoLMAuuk.d.ts.map +0 -1
- package/dist/index-DdspxNAA.d.ts +0 -481
- package/dist/index-DdspxNAA.d.ts.map +0 -1
- package/dist/index-Ds9dwqb5.d.cts +0 -481
- package/dist/index-Ds9dwqb5.d.cts.map +0 -1
- package/dist/payload-B2kFJyah.d.ts +0 -6
- package/dist/payload-B2kFJyah.d.ts.map +0 -1
- package/dist/payload-CJN8fTvX.d.cts +0 -6
- package/dist/payload-CJN8fTvX.d.cts.map +0 -1
- package/dist/payload-Claf2jUO.js +0 -13
- package/dist/payload-Claf2jUO.js.map +0 -1
- package/dist/payload-DpA5PA-w.cjs +0 -23
- package/dist/query/index.js.map +0 -1
- package/dist/queryBuilder-D2uwP9jI.js +0 -704
- package/dist/queryBuilder-D2uwP9jI.js.map +0 -1
- package/dist/queryBuilder-UW2MCaX4.cjs +0 -792
- package/dist/utils/index.d.cts.map +0 -1
- package/dist/utils/index.d.ts.map +0 -1
- package/dist/utils/index.js.map +0 -1
- package/src/actions/public/buildQuery.ts +0 -10
- package/src/actions/public/subscribeEntityEvents.ts +0 -119
- package/src/actions/wallet/updateEntity.ts +0 -60
- package/src/chains/braga.ts +0 -26
- package/src/chains/kaolin.ts +0 -29
- package/src/query/engine.test.ts +0 -526
- package/src/query/predicate.test.ts +0 -461
- package/src/query/predicate.ts +0 -144
- package/src/query/queryResult.test.ts +0 -270
- package/src/query/selectQueryBuilder.test.ts +0 -210
- package/src/types/attributes.ts +0 -16
- package/src/utils/validation.test.ts +0 -60
- package/src/utils/validation.ts +0 -32
- /package/dist/{types-H-TSmRpm.js → types-Bx1EKe0v.js} +0 -0
- /package/dist/{types-o3zWarRp.cjs → types-CKPddlfS.cjs} +0 -0
package/src/errors.ts
CHANGED
|
@@ -1,45 +1,50 @@
|
|
|
1
|
-
import { BLOCK_TIME } from "./consts"
|
|
2
|
-
|
|
3
1
|
export class EntityMutationError extends Error {
|
|
4
|
-
constructor(message: string) {
|
|
5
|
-
super(message)
|
|
2
|
+
constructor(message: string, options?: { cause?: unknown }) {
|
|
3
|
+
super(message, options)
|
|
6
4
|
this.name = "EntityMutationError"
|
|
7
5
|
}
|
|
8
6
|
}
|
|
9
|
-
export class
|
|
10
|
-
constructor(
|
|
11
|
-
super(
|
|
12
|
-
|
|
13
|
-
)
|
|
14
|
-
this.name = "InvalidExpirationError"
|
|
7
|
+
export class NoMoreResultsError extends Error {
|
|
8
|
+
constructor() {
|
|
9
|
+
super("No more results — the last page carried no cursor. Check hasNextPage() before next().")
|
|
10
|
+
this.name = "NoMoreResultsError"
|
|
15
11
|
}
|
|
16
12
|
}
|
|
17
|
-
|
|
18
|
-
|
|
13
|
+
|
|
14
|
+
export class NoEntityFoundError extends Error {
|
|
15
|
+
/** The key that matched nothing, when the lookup was by key. */
|
|
16
|
+
readonly entityKey: string | undefined
|
|
17
|
+
|
|
18
|
+
constructor(entityKey?: string) {
|
|
19
19
|
super(
|
|
20
|
-
|
|
20
|
+
entityKey === undefined
|
|
21
|
+
? "No entity found"
|
|
22
|
+
: `No live entity with key ${entityKey}. It was never created, or it has been deleted or ` +
|
|
23
|
+
"has expired.",
|
|
21
24
|
)
|
|
22
|
-
this.name = "
|
|
25
|
+
this.name = "NoEntityFoundError"
|
|
26
|
+
this.entityKey = entityKey
|
|
23
27
|
}
|
|
24
28
|
}
|
|
25
29
|
|
|
26
|
-
export class
|
|
27
|
-
|
|
28
|
-
super("No more results")
|
|
29
|
-
this.name = "NoMoreResultsError"
|
|
30
|
-
}
|
|
31
|
-
}
|
|
30
|
+
export class EmptyPatchError extends Error {
|
|
31
|
+
readonly entityKey: string
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
33
|
+
constructor(entityKey: string) {
|
|
34
|
+
super(
|
|
35
|
+
`The patch for entity ${entityKey} has nothing to apply. Pass "set" to write attributes, ` +
|
|
36
|
+
'"unset" to remove them, or "payload"/"contentType" to replace the contents.',
|
|
37
|
+
)
|
|
38
|
+
this.name = "EmptyPatchError"
|
|
39
|
+
this.entityKey = entityKey
|
|
37
40
|
}
|
|
38
41
|
}
|
|
39
42
|
|
|
40
|
-
export class
|
|
41
|
-
constructor() {
|
|
42
|
-
super(
|
|
43
|
-
|
|
43
|
+
export class InvalidContentTypeError extends Error {
|
|
44
|
+
constructor(contentType: string) {
|
|
45
|
+
super(
|
|
46
|
+
`Invalid content type "${contentType}". Must follow RFC 2045 MIME grammar and be lowercase only (e.g. "text/plain").`,
|
|
47
|
+
)
|
|
48
|
+
this.name = "InvalidContentTypeError"
|
|
44
49
|
}
|
|
45
50
|
}
|
package/src/index.ts
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
* @module main
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
+
// the attribute type system
|
|
6
|
+
export * from "./attr"
|
|
5
7
|
// export main arkiv stuff
|
|
6
8
|
export type { ArkivClient } from "./clients/baseClient"
|
|
7
9
|
export type { PublicArkivClient } from "./clients/createPublicClient"
|
|
@@ -10,10 +12,14 @@ export type { WalletArkivClient } from "./clients/createWalletClient"
|
|
|
10
12
|
export { createWalletClient } from "./clients/createWalletClient"
|
|
11
13
|
export type { PublicArkivActions } from "./clients/decorators/arkivPublic"
|
|
12
14
|
export type { WalletArkivActions } from "./clients/decorators/arkivWallet"
|
|
15
|
+
// entity lifetimes, flags, keys and the protocol parameters behind them
|
|
16
|
+
export * from "./entity"
|
|
13
17
|
// re-export errors
|
|
14
18
|
export * from "./errors"
|
|
15
19
|
// re-export arkiv types in main index file
|
|
16
20
|
export * from "./types"
|
|
17
21
|
// re-export chosen utils
|
|
18
22
|
export { chainFromName } from "./utils/chains"
|
|
23
|
+
// every create needs an expiry, and this is the only way to build one
|
|
24
|
+
export { type DeadlineOptions, ExpirationTime } from "./utils/expirationTime"
|
|
19
25
|
export { jsonToPayload, stringToPayload } from "./utils/payload"
|
package/src/query/engine.ts
CHANGED
|
@@ -1,153 +1,78 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { hexToNumber, numberToHex } from "viem"
|
|
1
|
+
import { numberToHex } from "viem"
|
|
3
2
|
import type { ArkivClient } from "../clients/baseClient"
|
|
4
|
-
import type {
|
|
3
|
+
import type { Entity } from "../types/entity"
|
|
4
|
+
import type { RpcQueryOptions, RpcSelect } from "../types/rpcSchema"
|
|
5
|
+
import { entityFromRpcResult } from "../utils/entities"
|
|
5
6
|
import { getLogger } from "../utils/logger"
|
|
6
|
-
import
|
|
7
|
+
import { asQueryError } from "./errors"
|
|
7
8
|
|
|
8
9
|
const logger = getLogger("query:engine")
|
|
9
10
|
|
|
10
|
-
|
|
11
|
-
const processValue = (value: string | number) => {
|
|
12
|
-
if (typeof value === "string") {
|
|
13
|
-
return `"${value}"`
|
|
14
|
-
}
|
|
15
|
-
return value
|
|
16
|
-
}
|
|
17
|
-
return predicates
|
|
18
|
-
.map((predicate) => {
|
|
19
|
-
switch (predicate.type) {
|
|
20
|
-
case "eq":
|
|
21
|
-
return `${predicate.key} = ${processValue(predicate.value)}`
|
|
22
|
-
case "neq":
|
|
23
|
-
return `${predicate.key} != ${processValue(predicate.value)}`
|
|
24
|
-
case "gt":
|
|
25
|
-
return `${predicate.key} > ${processValue(predicate.value)}`
|
|
26
|
-
case "gte":
|
|
27
|
-
return `${predicate.key} >= ${processValue(predicate.value)}`
|
|
28
|
-
case "lt":
|
|
29
|
-
return `${predicate.key} < ${processValue(predicate.value)}`
|
|
30
|
-
case "lte":
|
|
31
|
-
return `${predicate.key} <= ${processValue(predicate.value)}`
|
|
32
|
-
case "not":
|
|
33
|
-
return `!${predicate.key}`
|
|
34
|
-
case "or":
|
|
35
|
-
return `(${predicate.predicates.map((predicate) => processPredicates([predicate])).join(" || ")})`
|
|
36
|
-
case "and":
|
|
37
|
-
return `(${predicate.predicates.map((predicate) => processPredicates([predicate])).join(" && ")})`
|
|
38
|
-
default:
|
|
39
|
-
return ""
|
|
40
|
-
}
|
|
41
|
-
})
|
|
42
|
-
.join(" && ")
|
|
43
|
-
}
|
|
11
|
+
export const MAX_LIMIT = 200
|
|
44
12
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Fully-resolved include-data for fine-grained selection. When provided it takes
|
|
60
|
-
* precedence over the `withAttributes`/`withMetadata`/`withPayload` booleans.
|
|
61
|
-
*/
|
|
62
|
-
includeData?: RpcIncludeData | undefined
|
|
63
|
-
},
|
|
64
|
-
) {
|
|
65
|
-
const {
|
|
66
|
-
predicates,
|
|
67
|
-
limit,
|
|
68
|
-
cursor,
|
|
69
|
-
ownedBy,
|
|
70
|
-
createdBy,
|
|
71
|
-
orderBy,
|
|
72
|
-
validAtBlock,
|
|
73
|
-
withAttributes,
|
|
74
|
-
withMetadata,
|
|
75
|
-
withPayload,
|
|
76
|
-
includeData,
|
|
77
|
-
} = queryParams
|
|
13
|
+
/** One `arkiv_query` call. */
|
|
14
|
+
export type QueryRequest = {
|
|
15
|
+
/** The query expression, already rendered. */
|
|
16
|
+
query: string
|
|
17
|
+
/** What to return. */
|
|
18
|
+
select: RpcSelect
|
|
19
|
+
/** Page size, up to {@link MAX_LIMIT}. Defaults to the node's own page size. */
|
|
20
|
+
limit?: number | undefined
|
|
21
|
+
/** Cursor from a previous page. */
|
|
22
|
+
cursor?: string | undefined
|
|
23
|
+
/** Block height to read at. Defaults to the head. */
|
|
24
|
+
atBlock?: bigint | undefined
|
|
25
|
+
}
|
|
78
26
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
withAttributes,
|
|
88
|
-
withMetadata,
|
|
89
|
-
withPayload,
|
|
90
|
-
includeData,
|
|
91
|
-
})
|
|
27
|
+
/** One page of results. */
|
|
28
|
+
export type QueryResponse = {
|
|
29
|
+
entities: Entity[]
|
|
30
|
+
/** The block the page was read at — the same for every page of a paginated query. */
|
|
31
|
+
blockNumber: bigint
|
|
32
|
+
/** The cursor for the next page, or `undefined` when this was the last one. */
|
|
33
|
+
cursor: string | undefined
|
|
34
|
+
}
|
|
92
35
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
}
|
|
36
|
+
/**
|
|
37
|
+
* Runs one query and decodes the page it returns.
|
|
38
|
+
*
|
|
39
|
+
* @throws {QueryError} If the node rejects the query.
|
|
40
|
+
*/
|
|
41
|
+
export async function runQuery(client: ArkivClient, request: QueryRequest): Promise<QueryResponse> {
|
|
42
|
+
const { query, select, limit, cursor, atBlock } = request
|
|
100
43
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
44
|
+
if (limit !== undefined) {
|
|
45
|
+
if (!Number.isInteger(limit) || limit < 1) {
|
|
46
|
+
throw new Error(`limit must be a positive integer, got ${limit}.`)
|
|
47
|
+
}
|
|
48
|
+
if (limit > MAX_LIMIT) {
|
|
49
|
+
throw new Error(
|
|
50
|
+
`limit ${limit} exceeds the node maximum of ${MAX_LIMIT}. Page through the results with ` +
|
|
51
|
+
"the cursor instead.",
|
|
52
|
+
)
|
|
53
|
+
}
|
|
105
54
|
}
|
|
106
55
|
|
|
107
56
|
const queryOptions: RpcQueryOptions = {
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
attributes: withAttributes ?? false,
|
|
113
|
-
payload: withPayload ?? false,
|
|
114
|
-
contentType: withMetadata ?? false,
|
|
115
|
-
expiration: withMetadata ?? false,
|
|
116
|
-
owner: withMetadata ?? false,
|
|
117
|
-
creator: withMetadata ?? false,
|
|
118
|
-
createdAtBlock: withMetadata ?? false,
|
|
119
|
-
lastModifiedAtBlock: withMetadata ?? false,
|
|
120
|
-
transactionIndexInBlock: withMetadata ?? false,
|
|
121
|
-
operationIndexInTransaction: withMetadata ?? false,
|
|
122
|
-
} as RpcIncludeData),
|
|
57
|
+
select,
|
|
58
|
+
...(atBlock !== undefined && { atBlock: numberToHex(atBlock) }),
|
|
59
|
+
...(limit !== undefined && { limit: numberToHex(limit) }),
|
|
60
|
+
...(cursor !== undefined && { cursor }),
|
|
123
61
|
}
|
|
124
62
|
|
|
125
|
-
|
|
126
|
-
queryOptions.atBlock = numberToHex(validAtBlock)
|
|
127
|
-
}
|
|
128
|
-
if (limit !== undefined) {
|
|
129
|
-
queryOptions.resultsPerPage = numberToHex(limit)
|
|
130
|
-
}
|
|
131
|
-
if (cursor !== undefined) {
|
|
132
|
-
queryOptions.cursor = cursor
|
|
133
|
-
}
|
|
134
|
-
if (orderBy !== undefined) {
|
|
135
|
-
queryOptions.orderBy = orderBy
|
|
136
|
-
}
|
|
63
|
+
logger("arkiv_query %s %o", query, queryOptions)
|
|
137
64
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
cursor: queryOptions.cursor,
|
|
144
|
-
})
|
|
65
|
+
const result = await client
|
|
66
|
+
.request({ method: "arkiv_query", params: [query, queryOptions] })
|
|
67
|
+
.catch((error: unknown) => {
|
|
68
|
+
throw asQueryError(error, query) ?? error
|
|
69
|
+
})
|
|
145
70
|
|
|
146
|
-
const result = await client.request({
|
|
147
|
-
method: "arkiv_query",
|
|
148
|
-
params: [query, queryOptions],
|
|
149
|
-
})
|
|
150
71
|
logger("Raw result from query %o", result)
|
|
151
72
|
|
|
152
|
-
return
|
|
73
|
+
return {
|
|
74
|
+
entities: result.data.map((rpcEntity) => entityFromRpcResult(rpcEntity)),
|
|
75
|
+
blockNumber: BigInt(result.blockNumber),
|
|
76
|
+
cursor: result.cursor,
|
|
77
|
+
}
|
|
153
78
|
}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import type { TypeTag } from "../attr"
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* A predicate asked for an operator the query language does not define for that type.
|
|
5
|
+
*
|
|
6
|
+
* The operator × type matrix is part of the language, not a policy: a range comparison on an
|
|
7
|
+
* equality-only type is a **parse error** at the node, not an empty result. Catching it here turns
|
|
8
|
+
* a round trip and an RPC error code into a stack trace pointing at the call site.
|
|
9
|
+
*
|
|
10
|
+
* | | `=` `!=` | `<` `<=` `>` `>=` | `STARTSWITH` |
|
|
11
|
+
* | --- | --- | --- | --- |
|
|
12
|
+
* | `bool` | yes | no | no |
|
|
13
|
+
* | `i32` `u256` `dec` | yes | yes | no |
|
|
14
|
+
* | `str` | yes | no | yes |
|
|
15
|
+
* | `addr` `key` `bytes32` | yes | no | no |
|
|
16
|
+
*/
|
|
17
|
+
export class UnsupportedOperatorError extends Error {
|
|
18
|
+
/** The attribute the predicate names. */
|
|
19
|
+
readonly attributeName: string
|
|
20
|
+
/** The operator, as it would have been written in the query. */
|
|
21
|
+
readonly operator: string
|
|
22
|
+
/** The type of the value it was applied to. */
|
|
23
|
+
readonly tag: TypeTag
|
|
24
|
+
|
|
25
|
+
constructor(attributeName: string, operator: string, tag: TypeTag, hint?: string) {
|
|
26
|
+
super(
|
|
27
|
+
`The query language does not define ${operator} for a ${tag}, so "${attributeName} ` +
|
|
28
|
+
`${operator} ..." cannot be expressed.${hint ? ` ${hint}` : ""}`,
|
|
29
|
+
)
|
|
30
|
+
this.name = "UnsupportedOperatorError"
|
|
31
|
+
this.attributeName = attributeName
|
|
32
|
+
this.operator = operator
|
|
33
|
+
this.tag = tag
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* A predicate could not be expressed in the query language for a reason other than its operator —
|
|
39
|
+
* a system attribute that is returned in results but cannot be filtered on, a system attribute
|
|
40
|
+
* compared against the wrong type, or a combinator with nothing to combine.
|
|
41
|
+
*/
|
|
42
|
+
export class InvalidPredicateError extends Error {
|
|
43
|
+
/** The attribute the predicate names, where there is one. */
|
|
44
|
+
readonly attributeName: string | undefined
|
|
45
|
+
|
|
46
|
+
constructor(message: string, attributeName?: string) {
|
|
47
|
+
super(message)
|
|
48
|
+
this.name = "InvalidPredicateError"
|
|
49
|
+
this.attributeName = attributeName
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* What a node rejected a query for. Part of the frozen RPC surface, so it is safe to branch on —
|
|
55
|
+
* a `"cursor"` failure means start the query again, a `"block"` failure means the node no longer
|
|
56
|
+
* retains that height, and the rest mean the query itself needs fixing.
|
|
57
|
+
*/
|
|
58
|
+
export type QueryErrorKind =
|
|
59
|
+
/** The query string does not parse. `data` locates it: `{ position, expected, got }`. */
|
|
60
|
+
| "parse"
|
|
61
|
+
/** A predicate is well-formed but ill-typed — a range operator on an equality-only type, an unknown tag. */
|
|
62
|
+
| "type"
|
|
63
|
+
/** A literal failed validation — an `i32` out of range, a bad EIP-55 checksum, more than 18 decimal places. */
|
|
64
|
+
| "literal"
|
|
65
|
+
/** A node budget was exceeded — query length, predicate count, nesting depth, bitmap operations. */
|
|
66
|
+
| "limits"
|
|
67
|
+
/** The cursor expired, is malformed, or belongs to a different query, block or selection. */
|
|
68
|
+
| "cursor"
|
|
69
|
+
/** `atBlock` is outside the range the node retains. */
|
|
70
|
+
| "block"
|
|
71
|
+
|
|
72
|
+
/** The JSON-RPC error codes `arkiv_query` answers with, and what each one means. */
|
|
73
|
+
const QUERY_ERROR_KINDS: Readonly<Record<number, QueryErrorKind>> = {
|
|
74
|
+
[-32001]: "parse",
|
|
75
|
+
[-32002]: "type",
|
|
76
|
+
[-32003]: "literal",
|
|
77
|
+
[-32004]: "limits",
|
|
78
|
+
[-32005]: "cursor",
|
|
79
|
+
[-32006]: "block",
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* The node rejected a query.
|
|
84
|
+
*
|
|
85
|
+
* These codes are part of the frozen `arkiv_query` surface, so {@link kind} and {@link data} are
|
|
86
|
+
* stable enough to branch on — which the underlying JSON-RPC error is not, because the codes
|
|
87
|
+
* overlap Ethereum's own EIP-1474 numbering and get wrapped accordingly on the way up.
|
|
88
|
+
*
|
|
89
|
+
* @example
|
|
90
|
+
* try {
|
|
91
|
+
* await result.next()
|
|
92
|
+
* } catch (error) {
|
|
93
|
+
* if (error instanceof QueryError && error.kind === "cursor") {
|
|
94
|
+
* // The page is gone — start the query over.
|
|
95
|
+
* }
|
|
96
|
+
* }
|
|
97
|
+
*/
|
|
98
|
+
export class QueryError extends Error {
|
|
99
|
+
/** Which failure this is. */
|
|
100
|
+
readonly kind: QueryErrorKind
|
|
101
|
+
/** The JSON-RPC error code. */
|
|
102
|
+
readonly code: number
|
|
103
|
+
/** The machine-readable detail the node sent, if any — e.g. `{ position, expected, got }`. */
|
|
104
|
+
readonly data: unknown
|
|
105
|
+
/** The query string that was rejected. */
|
|
106
|
+
readonly query: string
|
|
107
|
+
|
|
108
|
+
constructor(kind: QueryErrorKind, code: number, query: string, data: unknown, detail?: string) {
|
|
109
|
+
super(`Query rejected (${kind}, ${code})${detail ? `: ${detail}` : ""}. Query: ${query}`, {
|
|
110
|
+
cause: data,
|
|
111
|
+
})
|
|
112
|
+
this.name = "QueryError"
|
|
113
|
+
this.kind = kind
|
|
114
|
+
this.code = code
|
|
115
|
+
this.data = data
|
|
116
|
+
this.query = query
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Recognises an `arkiv_query` failure in whatever the transport threw.
|
|
122
|
+
*
|
|
123
|
+
* The codes are dug out of the error chain rather than read off the top: viem maps this range onto
|
|
124
|
+
* its own EIP-1474 error classes (`-32001` is its "resource not found"), so by the time an error
|
|
125
|
+
* surfaces the Arkiv meaning is one or more `cause` links down.
|
|
126
|
+
*
|
|
127
|
+
* @returns The typed error, or `undefined` if this was not a query rejection.
|
|
128
|
+
*/
|
|
129
|
+
export function asQueryError(error: unknown, query: string): QueryError | undefined {
|
|
130
|
+
let code: number | undefined
|
|
131
|
+
let data: unknown
|
|
132
|
+
let detail: string | undefined
|
|
133
|
+
|
|
134
|
+
let current: unknown = error
|
|
135
|
+
for (let depth = 0; current != null && depth < 8; depth++) {
|
|
136
|
+
const link = current as {
|
|
137
|
+
code?: unknown
|
|
138
|
+
data?: unknown
|
|
139
|
+
shortMessage?: unknown
|
|
140
|
+
cause?: unknown
|
|
141
|
+
}
|
|
142
|
+
if (code === undefined && typeof link.code === "number") code = link.code
|
|
143
|
+
if (data === undefined && link.data !== undefined) data = link.data
|
|
144
|
+
if (detail === undefined && typeof link.shortMessage === "string") detail = link.shortMessage
|
|
145
|
+
current = link.cause
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const kind = code === undefined ? undefined : QUERY_ERROR_KINDS[code]
|
|
149
|
+
if (kind === undefined || code === undefined) return undefined
|
|
150
|
+
return new QueryError(kind, code, query, data, detail)
|
|
151
|
+
}
|