@arkiv-network/sdk 0.7.0-dev.1 → 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 +22 -60
- 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 +676 -375
- package/dist/index.d.cts +247 -208
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +247 -208
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +626 -368
- 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 -150
- package/dist/query/index.d.cts +5 -3
- package/dist/query/index.d.ts +5 -3
- package/dist/query/index.js +6 -138
- 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 +3 -3
- package/dist/types/index.d.cts +7 -2
- package/dist/types/index.d.ts +7 -2
- package/dist/types/index.js +3 -3
- 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 +2 -2
- package/src/actions/wallet/createEntity.ts +99 -24
- package/src/actions/wallet/extendEntity.ts +37 -13
- package/src/actions/wallet/mutateEntities.test.ts +76 -0
- package/src/actions/wallet/mutateEntities.ts +19 -21
- 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 +86 -94
- 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 +26 -49
- package/src/index.ts +6 -0
- package/src/query/engine.ts +59 -139
- 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 +60 -52
- package/src/types/events.ts +59 -16
- package/src/types/index.ts +20 -17
- package/src/types/rpcSchema.ts +69 -37
- package/src/utils/arkivTransactions.test.ts +600 -155
- package/src/utils/arkivTransactions.ts +181 -190
- 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 +109 -37
- package/src/utils/expirationTime.ts +114 -10
- package/src/utils/logger.ts +2 -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-Bz0LZQZY.js +0 -7
- package/dist/consts-Bz0LZQZY.js.map +0 -1
- package/dist/consts-CxT1-RXU.cjs +0 -18
- package/dist/entity-BhSXaB23.cjs +0 -81
- package/dist/entity-CN41IOrq.d.cts +0 -401
- package/dist/entity-CN41IOrq.d.cts.map +0 -1
- package/dist/entity-DGfVyB-a.js +0 -71
- package/dist/entity-DGfVyB-a.js.map +0 -1
- package/dist/entity-NnEVlePN.d.ts +0 -401
- package/dist/entity-NnEVlePN.d.ts.map +0 -1
- package/dist/index-ClkpS6s_.d.ts +0 -481
- package/dist/index-ClkpS6s_.d.ts.map +0 -1
- package/dist/index-w4qop0Pe.d.cts +0 -481
- package/dist/index-w4qop0Pe.d.cts.map +0 -1
- package/dist/payload-BpNUPOnY.js +0 -13
- package/dist/payload-BpNUPOnY.js.map +0 -1
- package/dist/payload-C2KHOUoj.d.ts +0 -6
- package/dist/payload-C2KHOUoj.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-D8YLKcJj.cjs +0 -23
- package/dist/query/index.js.map +0 -1
- package/dist/queryBuilder-BE_XhLLa.js +0 -735
- package/dist/queryBuilder-BE_XhLLa.js.map +0 -1
- package/dist/queryBuilder-J37buGCi.cjs +0 -865
- 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 -93
- package/src/actions/wallet/updateEntity.ts +0 -59
- package/src/chains/braga.ts +0 -26
- package/src/chains/kaolin.ts +0 -29
- package/src/query/engine.test.ts +0 -571
- 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 -213
- package/src/types/attributes.ts +0 -28
- package/src/utils/validation.test.ts +0 -106
- package/src/utils/validation.ts +0 -80
- /package/dist/{types-Ci4BRClw.js → types-Bx1EKe0v.js} +0 -0
- /package/dist/{types-a8gm_IaQ.cjs → types-CKPddlfS.cjs} +0 -0
package/src/errors.ts
CHANGED
|
@@ -1,65 +1,42 @@
|
|
|
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 InvalidExpirationError extends Error {
|
|
10
|
-
constructor(expiresIn: unknown) {
|
|
11
|
-
super(
|
|
12
|
-
`Invalid expiresIn: ${String(expiresIn)}. expiresIn must be a positive integer and a multiple of the Arkiv block time (${BLOCK_TIME} seconds), because expiration is measured in whole blocks (1 block = ${BLOCK_TIME} seconds).`,
|
|
13
|
-
)
|
|
14
|
-
this.name = "InvalidExpirationError"
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
export class InvalidAttributeError extends Error {
|
|
18
|
-
constructor(key: string, value: number) {
|
|
19
|
-
const remedy = Number.isInteger(value)
|
|
20
|
-
? `Offset it into the non-negative range (e.g. store ${String(value)} + OFFSET, subtracting OFFSET on read) to keep numeric ordering/range queries; otherwise store it as a string (e.g. "${String(value)}")`
|
|
21
|
-
: `To keep numeric ordering/range queries, scale it to an integer (e.g. Math.round(${String(value)} * 1000), dividing by 1000 on read); otherwise store it as a string (e.g. "${String(value)}")`
|
|
22
|
-
super(
|
|
23
|
-
`Invalid numeric value for attribute "${key}": ${String(value)}. Numeric attribute values must be non-negative integers (stored on-chain as unsigned 256-bit). ${remedy}.`,
|
|
24
|
-
)
|
|
25
|
-
this.name = "InvalidAttributeError"
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export class InvalidAttributeKeyError extends Error {
|
|
30
|
-
constructor(key: string, reason: string) {
|
|
31
|
-
super(
|
|
32
|
-
`Invalid attribute key "${key}": ${reason}. Keys must be 1-32 bytes, start with a lowercase letter (a-z), and contain only a-z, 0-9, ".", "-" or "_".`,
|
|
33
|
-
)
|
|
34
|
-
this.name = "InvalidAttributeKeyError"
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export class DuplicateAttributeError extends Error {
|
|
39
|
-
constructor(key: string) {
|
|
40
|
-
super(`Duplicate attribute key "${key}". Attribute keys must be unique per entity.`)
|
|
41
|
-
this.name = "DuplicateAttributeError"
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
7
|
export class NoMoreResultsError extends Error {
|
|
46
8
|
constructor() {
|
|
47
|
-
super("No more results")
|
|
9
|
+
super("No more results — the last page carried no cursor. Check hasNextPage() before next().")
|
|
48
10
|
this.name = "NoMoreResultsError"
|
|
49
11
|
}
|
|
50
12
|
}
|
|
51
13
|
|
|
52
|
-
export class
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
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
|
+
super(
|
|
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.",
|
|
24
|
+
)
|
|
25
|
+
this.name = "NoEntityFoundError"
|
|
26
|
+
this.entityKey = entityKey
|
|
56
27
|
}
|
|
57
28
|
}
|
|
58
29
|
|
|
59
|
-
export class
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
30
|
+
export class EmptyPatchError extends Error {
|
|
31
|
+
readonly entityKey: string
|
|
32
|
+
|
|
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
|
|
63
40
|
}
|
|
64
41
|
}
|
|
65
42
|
|
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,158 +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 type { Predicate } from "./predicate"
|
|
7
|
+
import { asQueryError } from "./errors"
|
|
8
8
|
|
|
9
9
|
const logger = getLogger("query:engine")
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
const processValue = (value: string | number) => {
|
|
13
|
-
if (typeof value === "string") {
|
|
14
|
-
// 32-byte hex strings are stored as the EntityKey attribute type (see
|
|
15
|
-
// encodeAttribute), which the query language only matches against unquoted
|
|
16
|
-
// hex literals. All other strings — including shorter hex — are stored and
|
|
17
|
-
// compared as quoted strings.
|
|
18
|
-
return isEntityKey(value) ? value : `"${value}"`
|
|
19
|
-
}
|
|
20
|
-
return value
|
|
21
|
-
}
|
|
22
|
-
return predicates
|
|
23
|
-
.map((predicate) => {
|
|
24
|
-
switch (predicate.type) {
|
|
25
|
-
case "eq":
|
|
26
|
-
return `${predicate.key} = ${processValue(predicate.value)}`
|
|
27
|
-
case "neq":
|
|
28
|
-
return `${predicate.key} != ${processValue(predicate.value)}`
|
|
29
|
-
case "gt":
|
|
30
|
-
return `${predicate.key} > ${processValue(predicate.value)}`
|
|
31
|
-
case "gte":
|
|
32
|
-
return `${predicate.key} >= ${processValue(predicate.value)}`
|
|
33
|
-
case "lt":
|
|
34
|
-
return `${predicate.key} < ${processValue(predicate.value)}`
|
|
35
|
-
case "lte":
|
|
36
|
-
return `${predicate.key} <= ${processValue(predicate.value)}`
|
|
37
|
-
case "not":
|
|
38
|
-
return `!${predicate.key}`
|
|
39
|
-
case "or":
|
|
40
|
-
return `(${predicate.predicates.map((predicate) => processPredicates([predicate])).join(" || ")})`
|
|
41
|
-
case "and":
|
|
42
|
-
return `(${predicate.predicates.map((predicate) => processPredicates([predicate])).join(" && ")})`
|
|
43
|
-
default:
|
|
44
|
-
return ""
|
|
45
|
-
}
|
|
46
|
-
})
|
|
47
|
-
.join(" && ")
|
|
48
|
-
}
|
|
11
|
+
export const MAX_LIMIT = 200
|
|
49
12
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Fully-resolved include-data for fine-grained selection. When provided it takes
|
|
65
|
-
* precedence over the `withAttributes`/`withMetadata`/`withPayload` booleans.
|
|
66
|
-
*/
|
|
67
|
-
includeData?: RpcIncludeData | undefined
|
|
68
|
-
},
|
|
69
|
-
) {
|
|
70
|
-
const {
|
|
71
|
-
predicates,
|
|
72
|
-
limit,
|
|
73
|
-
cursor,
|
|
74
|
-
ownedBy,
|
|
75
|
-
createdBy,
|
|
76
|
-
orderBy,
|
|
77
|
-
validAtBlock,
|
|
78
|
-
withAttributes,
|
|
79
|
-
withMetadata,
|
|
80
|
-
withPayload,
|
|
81
|
-
includeData,
|
|
82
|
-
} = 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
|
+
}
|
|
83
26
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
withAttributes,
|
|
93
|
-
withMetadata,
|
|
94
|
-
withPayload,
|
|
95
|
-
includeData,
|
|
96
|
-
})
|
|
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
|
+
}
|
|
97
35
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
}
|
|
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
|
|
105
43
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
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
|
+
}
|
|
110
54
|
}
|
|
111
55
|
|
|
112
56
|
const queryOptions: RpcQueryOptions = {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
attributes: withAttributes ?? false,
|
|
118
|
-
payload: withPayload ?? false,
|
|
119
|
-
contentType: withMetadata ?? false,
|
|
120
|
-
expiration: withMetadata ?? false,
|
|
121
|
-
owner: withMetadata ?? false,
|
|
122
|
-
creator: withMetadata ?? false,
|
|
123
|
-
createdAtBlock: withMetadata ?? false,
|
|
124
|
-
lastModifiedAtBlock: withMetadata ?? false,
|
|
125
|
-
transactionIndexInBlock: withMetadata ?? false,
|
|
126
|
-
operationIndexInTransaction: withMetadata ?? false,
|
|
127
|
-
} as RpcIncludeData),
|
|
57
|
+
select,
|
|
58
|
+
...(atBlock !== undefined && { atBlock: numberToHex(atBlock) }),
|
|
59
|
+
...(limit !== undefined && { limit: numberToHex(limit) }),
|
|
60
|
+
...(cursor !== undefined && { cursor }),
|
|
128
61
|
}
|
|
129
62
|
|
|
130
|
-
|
|
131
|
-
queryOptions.atBlock = numberToHex(validAtBlock)
|
|
132
|
-
}
|
|
133
|
-
if (limit !== undefined) {
|
|
134
|
-
queryOptions.resultsPerPage = numberToHex(limit)
|
|
135
|
-
}
|
|
136
|
-
if (cursor !== undefined) {
|
|
137
|
-
queryOptions.cursor = cursor
|
|
138
|
-
}
|
|
139
|
-
if (orderBy !== undefined) {
|
|
140
|
-
queryOptions.orderBy = orderBy
|
|
141
|
-
}
|
|
63
|
+
logger("arkiv_query %s %o", query, queryOptions)
|
|
142
64
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
cursor: queryOptions.cursor,
|
|
149
|
-
})
|
|
65
|
+
const result = await client
|
|
66
|
+
.request({ method: "arkiv_query", params: [query, queryOptions] })
|
|
67
|
+
.catch((error: unknown) => {
|
|
68
|
+
throw asQueryError(error, query) ?? error
|
|
69
|
+
})
|
|
150
70
|
|
|
151
|
-
const result = await client.request({
|
|
152
|
-
method: "arkiv_query",
|
|
153
|
-
params: [query, queryOptions],
|
|
154
|
-
})
|
|
155
71
|
logger("Raw result from query %o", result)
|
|
156
72
|
|
|
157
|
-
return
|
|
73
|
+
return {
|
|
74
|
+
entities: result.data.map((rpcEntity) => entityFromRpcResult(rpcEntity)),
|
|
75
|
+
blockNumber: BigInt(result.blockNumber),
|
|
76
|
+
cursor: result.cursor,
|
|
77
|
+
}
|
|
158
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
|
+
}
|