@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
|
@@ -1,209 +1,104 @@
|
|
|
1
1
|
import type { Hex } from "viem"
|
|
2
|
+
import { addr } from "../attr"
|
|
2
3
|
import type { ArkivClient } from "../clients/baseClient"
|
|
3
4
|
import type { Entity } from "../types/entity"
|
|
4
|
-
import type {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import type
|
|
5
|
+
import type { RpcSelect } from "../types/rpcSchema"
|
|
6
|
+
import { type QueryRequest, runQuery } from "./engine"
|
|
7
|
+
import { InvalidPredicateError } from "./errors"
|
|
8
|
+
import { and, type Expression, eq } from "./expression"
|
|
8
9
|
import { QueryResult } from "./queryResult"
|
|
9
|
-
import { type SelectArg,
|
|
10
|
+
import { type SelectArg, toRpcSelect } from "./selection"
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
|
-
*
|
|
13
|
-
* entities in JavaScript instead (e.g. `entities.sort(...)`). This type will be removed
|
|
14
|
-
* in a future release.
|
|
15
|
-
*/
|
|
16
|
-
export type OrderByAttribute = {
|
|
17
|
-
name: string
|
|
18
|
-
type: "string" | "number"
|
|
19
|
-
order: "asc" | "desc"
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Helper function to create an ascending order by attribute
|
|
24
|
-
* @param attributeName - The name of the attribute to order by
|
|
25
|
-
* @param attributeType - The type of the attribute to order by (string or number)
|
|
26
|
-
* @returns Input for orderBy method
|
|
13
|
+
* Builds and runs a query.
|
|
27
14
|
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
15
|
+
* The selection is fixed when the builder is created and the filter is added by chaining, so the
|
|
16
|
+
* result type is known before a single predicate is written. Create one with `client.select(...)`
|
|
17
|
+
* rather than constructing it directly — that is what infers the entity shape from the selection.
|
|
31
18
|
*
|
|
32
|
-
* @
|
|
33
|
-
*
|
|
34
|
-
*/
|
|
35
|
-
export function asc(attributeName: string, attributeType: "string" | "number"): OrderByAttribute {
|
|
36
|
-
return {
|
|
37
|
-
name: attributeName,
|
|
38
|
-
type: attributeType,
|
|
39
|
-
order: "asc",
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Helper function to create a descending order by attribute
|
|
45
|
-
* @param attributeName - The name of the attribute to order by
|
|
46
|
-
* @param attributeType - The type of the attribute to order by (string or number)
|
|
47
|
-
* @returns Input for orderBy method
|
|
19
|
+
* Every filter added with {@link where}, {@link ownedBy} and {@link createdBy} must hold; use
|
|
20
|
+
* {@link or} for alternatives inside one call.
|
|
48
21
|
*
|
|
49
|
-
* @
|
|
50
|
-
* helper) have no effect on the returned order. Sort the fetched entities in JavaScript
|
|
51
|
-
* instead (e.g. `entities.sort(...)`). This function will be removed in a future release.
|
|
22
|
+
* @typeParam TEntity - The projected entity shape, inferred from the selection by `client.select()`.
|
|
52
23
|
*
|
|
53
24
|
* @example
|
|
54
|
-
*
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Data-selection parameters forwarded to the query engine on each fetch.
|
|
66
|
-
* Subclasses of {@link BaseQueryBuilder} decide which parts of an entity are fetched.
|
|
67
|
-
*/
|
|
68
|
-
type SelectionParams = {
|
|
69
|
-
withAttributes?: boolean | undefined
|
|
70
|
-
withMetadata?: boolean | undefined
|
|
71
|
-
withPayload?: boolean | undefined
|
|
72
|
-
includeData?: RpcIncludeData | undefined
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* BaseQueryBuilder holds the query-building logic shared by every query builder
|
|
77
|
-
* (filtering, ordering, pagination, execution). It follows the Builder pattern,
|
|
78
|
-
* allowing methods to be chained. Subclasses decide how data selection is expressed
|
|
79
|
-
* by implementing the protected `selectionParams` method.
|
|
25
|
+
* import { createPublicClient } from "@arkiv-network/sdk"
|
|
26
|
+
* import { i32 } from "@arkiv-network/sdk/attr"
|
|
27
|
+
* import { cheesecake } from "@arkiv-network/sdk/chains"
|
|
28
|
+
* import { eq, gte, or } from "@arkiv-network/sdk/query"
|
|
29
|
+
* import { http } from "viem"
|
|
30
|
+
*
|
|
31
|
+
* const client = createPublicClient({ chain: cheesecake, transport: http() })
|
|
80
32
|
*
|
|
81
|
-
*
|
|
33
|
+
* const page = await client
|
|
34
|
+
* .select({ key: true, attributes: true })
|
|
35
|
+
* .where(gte("level", i32(10)), or(eq("status", "open"), eq("status", "review")))
|
|
36
|
+
* .ownedBy(owner)
|
|
37
|
+
* .limit(100)
|
|
38
|
+
* .fetch()
|
|
82
39
|
*
|
|
83
|
-
*
|
|
40
|
+
* for (const entity of page.entities) console.log(entity.key, entity.attributes)
|
|
84
41
|
*/
|
|
85
|
-
export
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
42
|
+
export class SelectQueryBuilder<TEntity = Entity> {
|
|
43
|
+
private readonly _client: ArkivClient
|
|
44
|
+
private readonly _select: RpcSelect
|
|
45
|
+
private readonly _filters: Expression[] = []
|
|
46
|
+
private _ownedBy: Expression | undefined
|
|
47
|
+
private _createdBy: Expression | undefined
|
|
48
|
+
private _limit: number | undefined
|
|
49
|
+
private _cursor: string | undefined
|
|
50
|
+
private _atBlock: bigint | undefined
|
|
94
51
|
|
|
95
|
-
constructor(client: ArkivClient) {
|
|
52
|
+
constructor(client: ArkivClient, selection?: SelectArg) {
|
|
96
53
|
this._client = client
|
|
97
|
-
this.
|
|
54
|
+
this._select = toRpcSelect(selection)
|
|
98
55
|
}
|
|
99
56
|
|
|
100
57
|
/**
|
|
101
|
-
*
|
|
102
|
-
*
|
|
103
|
-
* @
|
|
58
|
+
* Adds filters. Everything passed here, and across repeated calls, must hold.
|
|
59
|
+
*
|
|
60
|
+
* @param expressions - The expressions, as separate arguments or one array.
|
|
104
61
|
*
|
|
105
62
|
* @example
|
|
106
|
-
*
|
|
107
|
-
* builder.
|
|
63
|
+
* builder.where(eq("category", "docs"))
|
|
64
|
+
* builder.where(gte("level", i32(10)), lt("level", i32(20)))
|
|
65
|
+
* builder.where(or(eq("status", "open"), not(exists("closedAt"))))
|
|
108
66
|
*/
|
|
109
|
-
|
|
110
|
-
|
|
67
|
+
where(expressions: readonly Expression[]): this
|
|
68
|
+
where(...expressions: Expression[]): this
|
|
69
|
+
where(...expressions: (Expression | readonly Expression[])[]): this {
|
|
70
|
+
this._filters.push(...expressions.flat())
|
|
111
71
|
return this
|
|
112
72
|
}
|
|
113
73
|
|
|
114
74
|
/**
|
|
115
|
-
*
|
|
116
|
-
*
|
|
117
|
-
* @returns The query builder instance
|
|
75
|
+
* Restricts the results to entities this account owns — shorthand for
|
|
76
|
+
* `where(eq("$owner", addr(owner)))`. Calling it again replaces the filter.
|
|
118
77
|
*
|
|
119
78
|
* @example
|
|
120
|
-
*
|
|
121
|
-
* builder.createdBy("0x1234567890123456789012345678901234567890")
|
|
79
|
+
* builder.ownedBy("0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045")
|
|
122
80
|
*/
|
|
123
|
-
|
|
124
|
-
this.
|
|
81
|
+
ownedBy(owner: Hex): this {
|
|
82
|
+
this._ownedBy = eq("$owner", addr(owner))
|
|
125
83
|
return this
|
|
126
84
|
}
|
|
127
85
|
|
|
128
86
|
/**
|
|
129
|
-
*
|
|
130
|
-
*
|
|
131
|
-
* The order of the attributes is important. The first attribute is the primary order by attribute.
|
|
132
|
-
* You can use the helper functions asc() and desc() as input for this method.
|
|
133
|
-
* @param attributeName - The name of the attribute to order by
|
|
134
|
-
* @param attributeType - The type of the attribute to order by (string or number)
|
|
135
|
-
* @param order - The order to set the order by (asc or desc)
|
|
136
|
-
* @returns The query builder instance
|
|
137
|
-
*
|
|
138
|
-
* @deprecated Server-side ordering is not supported by the network, so this method has no
|
|
139
|
-
* effect on the returned order. Sort the fetched entities in JavaScript instead, e.g.
|
|
140
|
-
* `result.entities.sort((a, b) => ...)`. This method will be removed in a future release.
|
|
141
|
-
*
|
|
142
|
-
* @example
|
|
143
|
-
* const builder = client.select()
|
|
144
|
-
* builder.orderBy("name", "string", "desc")
|
|
145
|
-
* builder.orderBy(asc("name", "string"))
|
|
146
|
-
* builder.orderBy(desc("name", "string"))
|
|
147
|
-
*/
|
|
148
|
-
orderBy(attributeName: string, attributeType: "string" | "number", order?: "asc" | "desc"): this
|
|
149
|
-
/**
|
|
150
|
-
* Sets the orderBy for the query.
|
|
151
|
-
* This method takes the OrderByAttribute object as an argument and is mainly
|
|
152
|
-
* used to use the helper functions asc() and desc() to create the OrderByAttribute instances.
|
|
153
|
-
* @param orderByAttribute - The OrderByAttribute instance to set
|
|
154
|
-
* @returns The query builder instance
|
|
155
|
-
*
|
|
156
|
-
* @deprecated Server-side ordering is not supported by the network, so this method has no
|
|
157
|
-
* effect on the returned order. Sort the fetched entities in JavaScript instead, e.g.
|
|
158
|
-
* `result.entities.sort((a, b) => ...)`. This method will be removed in a future release.
|
|
87
|
+
* Restricts the results to entities this account created — shorthand for
|
|
88
|
+
* `where(eq("$creator", addr(creator)))`. Calling it again replaces the filter.
|
|
159
89
|
*
|
|
160
|
-
*
|
|
161
|
-
* const builder = client.select()
|
|
162
|
-
* builder.orderBy(asc("name", "string"))
|
|
163
|
-
* builder.orderBy(desc("name", "string"))
|
|
90
|
+
* The creator never changes; the owner does, so these differ after a transfer.
|
|
164
91
|
*/
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
attributeNameOrOrderByAttribute: string | OrderByAttribute,
|
|
168
|
-
attributeType?: "string" | "number",
|
|
169
|
-
order: "asc" | "desc" = "asc",
|
|
170
|
-
): this {
|
|
171
|
-
if (!this._orderBy) {
|
|
172
|
-
this._orderBy = []
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
const pushOrderByAttribute = ({ name, type, order }: OrderByAttribute) => {
|
|
176
|
-
this._orderBy?.push({
|
|
177
|
-
name,
|
|
178
|
-
type: type === "number" ? "numeric" : type,
|
|
179
|
-
desc: order === "desc",
|
|
180
|
-
})
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
if (typeof attributeNameOrOrderByAttribute === "string") {
|
|
184
|
-
if (!attributeType) {
|
|
185
|
-
throw new Error("attributeType is required when using positional orderBy arguments")
|
|
186
|
-
}
|
|
187
|
-
pushOrderByAttribute({
|
|
188
|
-
name: attributeNameOrOrderByAttribute,
|
|
189
|
-
type: attributeType,
|
|
190
|
-
order,
|
|
191
|
-
})
|
|
192
|
-
} else {
|
|
193
|
-
pushOrderByAttribute(attributeNameOrOrderByAttribute)
|
|
194
|
-
}
|
|
195
|
-
|
|
92
|
+
createdBy(creator: Hex): this {
|
|
93
|
+
this._createdBy = eq("$creator", addr(creator))
|
|
196
94
|
return this
|
|
197
95
|
}
|
|
198
96
|
|
|
199
97
|
/**
|
|
200
|
-
* Sets the
|
|
201
|
-
* @param limit - The number of entities to return
|
|
202
|
-
* @returns The query builder instance
|
|
98
|
+
* Sets the page size, up to the node maximum of 200. Without it the node picks the page size.
|
|
203
99
|
*
|
|
204
100
|
* @example
|
|
205
|
-
*
|
|
206
|
-
* builder.limit(10)
|
|
101
|
+
* builder.limit(100)
|
|
207
102
|
*/
|
|
208
103
|
limit(limit: number): this {
|
|
209
104
|
this._limit = limit
|
|
@@ -211,13 +106,11 @@ export abstract class BaseQueryBuilder<TEntity> {
|
|
|
211
106
|
}
|
|
212
107
|
|
|
213
108
|
/**
|
|
214
|
-
*
|
|
215
|
-
* @param cursor - The cursor to set which tells to RPC Query server where to start or continue the query.
|
|
216
|
-
* @returns The query builder instance
|
|
109
|
+
* Starts from a cursor returned by an earlier page.
|
|
217
110
|
*
|
|
218
|
-
* @
|
|
219
|
-
*
|
|
220
|
-
*
|
|
111
|
+
* Pagination normally goes through {@link QueryResult.next}, which carries the cursor for you;
|
|
112
|
+
* this is for resuming a walk in a later process. A cursor is bound to the query, block and
|
|
113
|
+
* selection it came from, so it must be used with an identically-built query.
|
|
221
114
|
*/
|
|
222
115
|
cursor(cursor: string): this {
|
|
223
116
|
this._cursor = cursor
|
|
@@ -225,223 +118,97 @@ export abstract class BaseQueryBuilder<TEntity> {
|
|
|
225
118
|
}
|
|
226
119
|
|
|
227
120
|
/**
|
|
228
|
-
*
|
|
229
|
-
*
|
|
230
|
-
* @param validAtBlock - The block number to set
|
|
231
|
-
* @returns The query builder instance
|
|
121
|
+
* Reads the state as of a given block rather than the head. The block must be within the range
|
|
122
|
+
* the node retains.
|
|
232
123
|
*
|
|
233
124
|
* @example
|
|
234
|
-
*
|
|
235
|
-
* builder.validAtBlock(10000)
|
|
125
|
+
* builder.atBlock(1_297_000n)
|
|
236
126
|
*/
|
|
237
|
-
|
|
238
|
-
this.
|
|
127
|
+
atBlock(block: bigint): this {
|
|
128
|
+
this._atBlock = block
|
|
239
129
|
return this
|
|
240
130
|
}
|
|
241
131
|
|
|
242
132
|
/**
|
|
243
|
-
*
|
|
244
|
-
* multiple predicates passed as separate arguments, or an array of predicates - all combined with 'and'.
|
|
245
|
-
* Predicates can be nested using 'or' and 'and' predicates.
|
|
246
|
-
* @param predicates - The predicates to set, either as a single array or as separate arguments
|
|
247
|
-
* @returns The query builder instance
|
|
133
|
+
* The query string this builder will send — the same text the node parses.
|
|
248
134
|
*
|
|
249
|
-
* @
|
|
250
|
-
* const builder = client.select()
|
|
251
|
-
* builder.where(eq("name", "John"))
|
|
252
|
-
* builder.where(eq("name", "John"), eq("age", 30))
|
|
253
|
-
* builder.where([eq("name", "John"), eq("age", 30)])
|
|
254
|
-
* builder.where(eq("name", "John"), or(eq("age", 30), eq("age", 31)))
|
|
255
|
-
* builder.where(eq("name", "John"), and(eq("age", 30), eq("age", 31)))
|
|
256
|
-
* builder.where(eq("name", "John"), or(eq("age", 30), and(eq("age", 31), eq("age", 32))))
|
|
257
|
-
* builder.where(eq("name", "John"), and(eq("age", 30), or(eq("age", 31), eq("age", 32))))
|
|
135
|
+
* @throws {InvalidPredicateError} If no filter has been added.
|
|
258
136
|
*/
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
this.
|
|
263
|
-
|
|
137
|
+
toString(): string {
|
|
138
|
+
const filters = [...this._filters]
|
|
139
|
+
if (this._ownedBy) filters.push(this._ownedBy)
|
|
140
|
+
if (this._createdBy) filters.push(this._createdBy)
|
|
141
|
+
if (filters.length === 0) {
|
|
142
|
+
throw new InvalidPredicateError(
|
|
143
|
+
"A query needs at least one filter — the language has no spelling for 'match every " +
|
|
144
|
+
'entity". Add a where(...) predicate, or narrow by ownedBy(...) / createdBy(...).',
|
|
145
|
+
)
|
|
146
|
+
}
|
|
147
|
+
return and(filters).toString()
|
|
264
148
|
}
|
|
265
149
|
|
|
266
150
|
/**
|
|
267
|
-
*
|
|
268
|
-
* Subclasses decide which parts of an entity are fetched.
|
|
269
|
-
*/
|
|
270
|
-
protected abstract selectionParams(): SelectionParams
|
|
271
|
-
|
|
272
|
-
/**
|
|
273
|
-
* Builds a single result entity from a raw RPC entity. Subclasses decide the produced shape
|
|
274
|
-
* (a full {@link Entity}, or a projected object inferred from the selection).
|
|
275
|
-
*/
|
|
276
|
-
protected abstract projectEntity(rpcEntity: RpcEntity): TEntity | Promise<TEntity>
|
|
277
|
-
|
|
278
|
-
/**
|
|
279
|
-
* Fetches the entities from the query.
|
|
280
|
-
* It will return a QueryResult instance which can be used to fetch the next and previous pages.
|
|
281
|
-
* @returns The QueryResult instance {@link QueryResult}
|
|
151
|
+
* Runs the query and returns the first page.
|
|
282
152
|
*
|
|
283
|
-
* @
|
|
284
|
-
*
|
|
285
|
-
* const result = await builder.where(eq("name", "John")).fetch()
|
|
286
|
-
* // result = { entities: [Entity, Entity, Entity], next: async () => QueryResult, previous: async () => QueryResult }
|
|
287
|
-
*/
|
|
288
|
-
async fetch(): Promise<QueryResult<TEntity>> {
|
|
289
|
-
const queryResult = await processQuery(this._client, {
|
|
290
|
-
predicates: this._predicates,
|
|
291
|
-
limit: this._limit,
|
|
292
|
-
cursor: this._cursor,
|
|
293
|
-
ownedBy: this._ownedBy,
|
|
294
|
-
createdBy: this._createdBy,
|
|
295
|
-
orderBy: this._orderBy,
|
|
296
|
-
validAtBlock: this._validAtBlock,
|
|
297
|
-
...this.selectionParams(),
|
|
298
|
-
})
|
|
299
|
-
|
|
300
|
-
const entities = await Promise.all(queryResult.data.map((entity) => this.projectEntity(entity)))
|
|
301
|
-
|
|
302
|
-
this.cursor(queryResult.cursor)
|
|
303
|
-
this.validAtBlock(BigInt(queryResult.blockNumber ?? 0))
|
|
304
|
-
|
|
305
|
-
return new QueryResult<TEntity>(entities, this, this._cursor, this._limit, this._validAtBlock)
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
/**
|
|
309
|
-
* Counts the entities from the query.
|
|
310
|
-
* @returns The number of entities
|
|
153
|
+
* @throws {InvalidPredicateError} If no filter has been added.
|
|
154
|
+
* @throws {QueryError} If the node rejects the query.
|
|
311
155
|
*
|
|
312
156
|
* @example
|
|
313
|
-
* const
|
|
314
|
-
*
|
|
315
|
-
* //
|
|
157
|
+
* const page = await client.select({ key: true }).where(eq("category", "docs")).fetch()
|
|
158
|
+
* page.entities // this page
|
|
159
|
+
* await page.next() // the next one
|
|
316
160
|
*/
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
161
|
+
// Async so that a builder with no filter rejects like every other failure here, rather than
|
|
162
|
+
// throwing synchronously out of a call the caller is awaiting.
|
|
163
|
+
async fetch(): Promise<QueryResult<TEntity>> {
|
|
164
|
+
// Snapshot the request here, so the pages of one walk are all pages of the *same* query. The
|
|
165
|
+
// builder stays mutable and reusable; a cursor is bound to the query, block and selection it
|
|
166
|
+
// was issued for, so re-reading a since-edited builder would page with a mismatched cursor.
|
|
167
|
+
const request = Object.freeze({
|
|
168
|
+
query: this.toString(),
|
|
169
|
+
select: this._select,
|
|
320
170
|
limit: this._limit,
|
|
321
|
-
|
|
322
|
-
ownedBy: this._ownedBy,
|
|
323
|
-
createdBy: this._createdBy,
|
|
324
|
-
orderBy: undefined,
|
|
325
|
-
validAtBlock: this._validAtBlock,
|
|
326
|
-
withAttributes: false,
|
|
327
|
-
withMetadata: false,
|
|
328
|
-
withPayload: false,
|
|
171
|
+
atBlock: this._atBlock,
|
|
329
172
|
})
|
|
330
|
-
|
|
331
|
-
return queryResult.data.length ?? 0
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
/**
|
|
336
|
-
* QueryBuilder is a helper class to build queries to the Arkiv DBChains.
|
|
337
|
-
* It can be used to fetch entities from the Arkiv DBChains. It follows the Builder pattern allowing chaining of methods.
|
|
338
|
-
*
|
|
339
|
-
* By default the result includes only the entity `key`. Additional data is opt-in through
|
|
340
|
-
* `withAttributes()`, `withMetadata()` and `withPayload()`.
|
|
341
|
-
*
|
|
342
|
-
* @deprecated Use {@link SelectQueryBuilder} via `client.select()` instead. Declaring the
|
|
343
|
-
* selection up front avoids the common mistake of forgetting to opt in to data and getting back
|
|
344
|
-
* entities with only their `key` populated. This class remains for backwards compatibility and
|
|
345
|
-
* will be removed in a future release.
|
|
346
|
-
*
|
|
347
|
-
* @param client - The Arkiv client
|
|
348
|
-
* @returns The QueryBuilder instance {@link QueryBuilder}
|
|
349
|
-
*/
|
|
350
|
-
export class QueryBuilder extends BaseQueryBuilder<Entity> {
|
|
351
|
-
private _withAttributes: boolean | undefined
|
|
352
|
-
private _withMetadata: boolean | undefined
|
|
353
|
-
private _withPayload: boolean | undefined
|
|
354
|
-
|
|
355
|
-
/**
|
|
356
|
-
* Sets the withAttributes flag which will return the attributes for the entities if true
|
|
357
|
-
* @param withAttributes - The boolean value to set
|
|
358
|
-
* @returns The QueryBuilder instance
|
|
359
|
-
*
|
|
360
|
-
* @example
|
|
361
|
-
* const builder = client.buildQuery()
|
|
362
|
-
* builder.withAttributes(true)
|
|
363
|
-
*/
|
|
364
|
-
withAttributes(withAttributes: boolean = true): this {
|
|
365
|
-
this._withAttributes = withAttributes
|
|
366
|
-
return this
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
/**
|
|
370
|
-
* Sets the withMetadata flag which will return the metadata (like owner, expiredAt, etc.) for the entities if true
|
|
371
|
-
* @param withMetadata - The boolean value to set
|
|
372
|
-
* @returns The QueryBuilder instance
|
|
373
|
-
*
|
|
374
|
-
* @example
|
|
375
|
-
* const builder = client.buildQuery()
|
|
376
|
-
* builder.withMetadata(true)
|
|
377
|
-
*/
|
|
378
|
-
withMetadata(withMetadata: boolean = true): this {
|
|
379
|
-
this._withMetadata = withMetadata
|
|
380
|
-
return this
|
|
173
|
+
return fetchPage<TEntity>(this._client, request, this._cursor)
|
|
381
174
|
}
|
|
382
175
|
|
|
383
176
|
/**
|
|
384
|
-
*
|
|
385
|
-
* @param withPayload - The boolean value to set
|
|
386
|
-
* @returns The QueryBuilder instance
|
|
177
|
+
* Walks every page, yielding one entity at a time.
|
|
387
178
|
*
|
|
388
179
|
* @example
|
|
389
|
-
* const
|
|
390
|
-
*
|
|
180
|
+
* for await (const entity of client.select({ key: true }).where(eq("category", "docs"))) {
|
|
181
|
+
* console.log(entity.key)
|
|
182
|
+
* }
|
|
391
183
|
*/
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
protected selectionParams(): SelectionParams {
|
|
398
|
-
return {
|
|
399
|
-
withAttributes: this._withAttributes,
|
|
400
|
-
withMetadata: this._withMetadata,
|
|
401
|
-
withPayload: this._withPayload,
|
|
184
|
+
async *[Symbol.asyncIterator](): AsyncGenerator<TEntity> {
|
|
185
|
+
let page: QueryResult<TEntity> | undefined = await this.fetch()
|
|
186
|
+
while (page) {
|
|
187
|
+
yield* page.entities
|
|
188
|
+
page = page.hasNextPage() ? await page.next() : undefined
|
|
402
189
|
}
|
|
403
190
|
}
|
|
404
|
-
|
|
405
|
-
protected projectEntity(rpcEntity: RpcEntity): Promise<Entity> {
|
|
406
|
-
return entityFromRpcResult(rpcEntity)
|
|
407
|
-
}
|
|
408
191
|
}
|
|
409
192
|
|
|
410
193
|
/**
|
|
411
|
-
*
|
|
412
|
-
* up front, so results always contain exactly the data you asked for — and the type of each
|
|
413
|
-
* returned entity is narrowed to exactly the selected fields.
|
|
414
|
-
*
|
|
415
|
-
* The selection is fixed at construction time and is flat — each field maps to an entity field.
|
|
416
|
-
* Create one via `client.select(...)` rather than constructing it directly, so the result type is
|
|
417
|
-
* inferred from the selection.
|
|
418
|
-
*
|
|
419
|
-
* @typeParam TEntity - The projected entity shape, inferred from the selection by `client.select()`.
|
|
194
|
+
* Fetches one page and wires up the next.
|
|
420
195
|
*
|
|
421
|
-
*
|
|
422
|
-
*
|
|
423
|
-
* await client.select().where(eq("name", "John")).fetch()
|
|
424
|
-
* await client.select("*").where(eq("name", "John")).fetch()
|
|
425
|
-
* // specific fields
|
|
426
|
-
* await client.select({ owner: true, attributes: true }).fetch()
|
|
427
|
-
* // a single field — result is typed (flat) { owner: Hex }
|
|
428
|
-
* await client.select({ owner: true }).fetch()
|
|
196
|
+
* Free-standing rather than a method: a page holds this closure for its lifetime, and closing over
|
|
197
|
+
* a frozen request keeps the builder — and everything it references — out of that reference.
|
|
429
198
|
*/
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
return (await entityFromRpcResult(rpcEntity)) as unknown as TEntity
|
|
446
|
-
}
|
|
199
|
+
async function fetchPage<TEntity>(
|
|
200
|
+
client: ArkivClient,
|
|
201
|
+
request: Readonly<Omit<QueryRequest, "cursor">>,
|
|
202
|
+
cursor: string | undefined,
|
|
203
|
+
): Promise<QueryResult<TEntity>> {
|
|
204
|
+
const response = await runQuery(client, { ...request, cursor })
|
|
205
|
+
|
|
206
|
+
return new QueryResult<TEntity>(
|
|
207
|
+
// Every row decodes to a full Entity; the static narrowing to the selected fields comes from
|
|
208
|
+
// `client.select()`'s inference and nothing at runtime depends on it.
|
|
209
|
+
response.entities as unknown as TEntity[],
|
|
210
|
+
response.blockNumber,
|
|
211
|
+
response.cursor,
|
|
212
|
+
(next) => fetchPage<TEntity>(client, request, next),
|
|
213
|
+
)
|
|
447
214
|
}
|