@arkiv-network/sdk 0.8.1-dev.1 → 0.9.0-next.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.
- package/README.md +180 -61
- package/dist/chains/index.d.ts +17 -17
- package/dist/changeOwnership-Cy9gfyjN.d.cts +1160 -0
- package/dist/changeOwnership-Cy9gfyjN.d.cts.map +1 -0
- package/dist/changeOwnership-gXf-lBqI.d.ts +1160 -0
- package/dist/changeOwnership-gXf-lBqI.d.ts.map +1 -0
- package/dist/entity/index.cjs +4 -10
- package/dist/entity/index.d.cts +3 -3
- package/dist/entity/index.d.ts +3 -3
- package/dist/entity/index.js +3 -4
- package/dist/entity-BM1unrQf.cjs +41 -0
- package/dist/{entity-DJ0Gml9c.cjs → entity-BgaFJSaV.cjs} +4 -4
- package/dist/entity-CFAI1KyG.js +36 -0
- package/dist/entity-CFAI1KyG.js.map +1 -0
- package/dist/{entity-CniBgBSd.js → entity-CGqx_AU0.js} +4 -4
- package/dist/entity-CGqx_AU0.js.map +1 -0
- package/dist/{payload-AfUfSctb.d.ts → expirationTime-C7PZK49K.d.ts} +8 -37
- package/dist/expirationTime-C7PZK49K.d.ts.map +1 -0
- package/dist/expirationTime-CkIGNAg8.cjs +140 -0
- package/dist/{payload-D167BlJR.d.cts → expirationTime-DOB8V4B8.d.cts} +8 -37
- package/dist/expirationTime-DOB8V4B8.d.cts.map +1 -0
- package/dist/{expiry-Cgdb_dbq.js → expirationTime-UKPuA-LM.js} +62 -27
- package/dist/expirationTime-UKPuA-LM.js.map +1 -0
- package/dist/expiry-B48I-yTA.d.cts +40 -0
- package/dist/expiry-B48I-yTA.d.cts.map +1 -0
- package/dist/expiry-S2PUL8Hg.d.ts +40 -0
- package/dist/expiry-S2PUL8Hg.d.ts.map +1 -0
- package/dist/index-C-mMF5WZ.d.cts +735 -0
- package/dist/index-C-mMF5WZ.d.cts.map +1 -0
- package/dist/{index-BkqSnGJH.d.ts → index-C4zZqJ9S.d.ts} +5 -27
- package/dist/index-C4zZqJ9S.d.ts.map +1 -0
- package/dist/{index-CLhsFyVH.d.cts → index-CcVWYB6v.d.cts} +5 -27
- package/dist/index-CcVWYB6v.d.cts.map +1 -0
- package/dist/index-DCx2XC6S.d.ts +489 -0
- package/dist/index-DCx2XC6S.d.ts.map +1 -0
- package/dist/index-DLb7BfIw.d.ts +735 -0
- package/dist/index-DLb7BfIw.d.ts.map +1 -0
- package/dist/index-DzriOUm8.d.cts +489 -0
- package/dist/index-DzriOUm8.d.cts.map +1 -0
- package/dist/index.cjs +162 -146
- package/dist/index.d.cts +180 -133
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +180 -133
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +93 -83
- package/dist/index.js.map +1 -1
- package/dist/params-BuIrJA9a.cjs +45 -0
- package/dist/params-DDJOnsNi.js +22 -0
- package/dist/params-DDJOnsNi.js.map +1 -0
- package/dist/query/index.cjs +9 -4
- package/dist/query/index.d.cts +6 -6
- package/dist/query/index.d.ts +6 -6
- package/dist/query/index.js +4 -5
- package/dist/query-D0KtuFh2.cjs +1625 -0
- package/dist/query-DpRDozmj.js +1417 -0
- package/dist/query-DpRDozmj.js.map +1 -0
- package/dist/schema/index.cjs +46 -0
- package/dist/schema/index.d.cts +2 -0
- package/dist/schema/index.d.ts +2 -0
- package/dist/schema/index.js +3 -0
- package/dist/{attr-BXTkpnoc.cjs → schema-B9AyXcgU.cjs} +513 -246
- package/dist/{attr-tmyREHtw.js → schema-DY4sqehl.js} +427 -244
- package/dist/schema-DY4sqehl.js.map +1 -0
- package/dist/types/index.cjs +1 -1
- package/dist/types/index.d.cts +5 -5
- package/dist/types/index.d.ts +5 -5
- package/dist/types/index.js +1 -1
- package/dist/utils/index.cjs +3 -5
- package/dist/utils/index.d.cts +3 -3
- package/dist/utils/index.d.ts +3 -3
- package/dist/utils/index.js +3 -3
- package/package.json +9 -12
- package/src/actions/public/getEntity.test.ts +69 -0
- package/src/actions/public/getEntity.ts +34 -20
- package/src/actions/public/predictEntityKeys.ts +8 -7
- package/src/actions/public/query.test.ts +58 -0
- package/src/actions/public/query.ts +41 -13
- package/src/actions/wallet/createEntity.ts +34 -63
- package/src/actions/wallet/executeBatch.test.ts +38 -0
- package/src/actions/wallet/executeBatch.ts +26 -9
- package/src/actions/wallet/extendEntity.ts +9 -8
- package/src/actions/wallet/patchEntity.ts +43 -52
- package/src/clients/createPublicClient.test.ts +10 -2
- package/src/clients/createWalletClient.ts +4 -1
- package/src/clients/decorators/arkivPublic.ts +123 -80
- package/src/clients/decorators/arkivWallet.ts +113 -65
- package/src/entity/create.test.ts +25 -21
- package/src/entity/expiry.ts +8 -6
- package/src/entity/flags.ts +1 -1
- package/src/entity/index.ts +2 -4
- package/src/entity/key.ts +3 -3
- package/src/entity/operations.ts +1 -1
- package/src/errors.ts +2 -11
- package/src/index.ts +18 -6
- package/src/query/builderBase.ts +166 -0
- package/src/query/countBuilder.test.ts +91 -0
- package/src/query/countBuilder.ts +67 -0
- package/src/query/engine.ts +9 -3
- package/src/query/errors.ts +24 -14
- package/src/query/expression.test.ts +129 -21
- package/src/query/expression.ts +323 -94
- package/src/query/index.ts +26 -4
- package/src/query/injection.test.ts +5 -11
- package/src/query/queryBuilder.test.ts +467 -58
- package/src/query/queryBuilder.ts +191 -98
- package/src/query/queryResult.ts +35 -9
- package/src/query/selection.test.ts +278 -24
- package/src/query/selection.ts +427 -86
- package/src/schema/attributes.test.ts +266 -0
- package/src/schema/attributes.ts +212 -0
- package/src/{attr → schema}/codec.test.ts +37 -5
- package/src/{attr → schema}/codec.ts +40 -11
- package/src/schema/column.ts +193 -0
- package/src/{attr → schema}/errors.ts +67 -10
- package/src/schema/index.ts +76 -0
- package/src/{attr → schema}/names.ts +4 -2
- package/src/schema/schema.test.ts +171 -0
- package/src/schema/schema.ts +250 -0
- package/src/{attr → schema}/types.ts +36 -30
- package/src/{attr → schema}/values.test.ts +62 -3
- package/src/{attr → schema}/values.ts +246 -65
- package/src/types/entity.test.ts +1 -44
- package/src/types/entity.ts +9 -55
- package/src/types/events.ts +1 -1
- package/src/types/index.ts +11 -4
- package/src/types/rpcSchema.ts +22 -16
- package/src/utils/arkivTransactions.test.ts +356 -116
- package/src/utils/arkivTransactions.ts +30 -38
- package/src/utils/entities.ts +10 -9
- package/src/utils/expirationTime.ts +5 -5
- package/src/utils/index.ts +0 -1
- package/src/utils/logger.test.ts +45 -0
- package/src/utils/logger.ts +5 -39
- package/src/utils/revert.test.ts +25 -0
- package/src/utils/revert.ts +28 -3
- package/dist/attr/index.cjs +0 -34
- package/dist/attr/index.d.cts +0 -2
- package/dist/attr/index.d.ts +0 -2
- package/dist/attr/index.js +0 -3
- package/dist/attr-tmyREHtw.js.map +0 -1
- package/dist/entity-B0t6sN7F.cjs +0 -71
- package/dist/entity-BIp_df9u.js +0 -67
- package/dist/entity-BIp_df9u.js.map +0 -1
- package/dist/entity-CniBgBSd.js.map +0 -1
- package/dist/expiry-BxOlvVWC.d.ts +0 -79
- package/dist/expiry-BxOlvVWC.d.ts.map +0 -1
- package/dist/expiry-CG9GhVXv.cjs +0 -135
- package/dist/expiry-Cgdb_dbq.js.map +0 -1
- package/dist/expiry-DRV29F1r.d.cts +0 -79
- package/dist/expiry-DRV29F1r.d.cts.map +0 -1
- package/dist/flags-DCD26uhe.cjs +0 -43
- package/dist/flags-DUT2gHEU.js +0 -33
- package/dist/flags-DUT2gHEU.js.map +0 -1
- package/dist/index-BkqSnGJH.d.ts.map +0 -1
- package/dist/index-C7fbakoI.d.cts +0 -278
- package/dist/index-C7fbakoI.d.cts.map +0 -1
- package/dist/index-CIcHPx2j.d.cts +0 -424
- package/dist/index-CIcHPx2j.d.cts.map +0 -1
- package/dist/index-CLhsFyVH.d.cts.map +0 -1
- package/dist/index-Cd2aaWme.d.ts +0 -424
- package/dist/index-Cd2aaWme.d.ts.map +0 -1
- package/dist/index-Cq6fa8AI.d.ts +0 -278
- package/dist/index-Cq6fa8AI.d.ts.map +0 -1
- package/dist/index-DaNeJUw8.d.ts +0 -955
- package/dist/index-DaNeJUw8.d.ts.map +0 -1
- package/dist/index-Dv16LO2s.d.cts +0 -955
- package/dist/index-Dv16LO2s.d.cts.map +0 -1
- package/dist/payload-AfUfSctb.d.ts.map +0 -1
- package/dist/payload-D167BlJR.d.cts.map +0 -1
- package/dist/payload-DE7wMztL.js +0 -92
- package/dist/payload-DE7wMztL.js.map +0 -1
- package/dist/payload-DqFweqiO.cjs +0 -108
- package/dist/query-9oojUdM7.cjs +0 -1128
- package/dist/query-Ber-lOJQ.js +0 -968
- package/dist/query-Ber-lOJQ.js.map +0 -1
- package/src/actions/public/getEntityCount.ts +0 -10
- package/src/attr/attributes.test.ts +0 -120
- package/src/attr/attributes.ts +0 -196
- package/src/attr/index.ts +0 -50
- package/src/types/mimeTypes.ts +0 -27
- package/src/utils/compression.test.ts +0 -32
- package/src/utils/compression.ts +0 -102
- package/src/utils/payload.ts +0 -36
- /package/src/{attr → schema}/decimal.ts +0 -0
|
@@ -7,11 +7,11 @@ import {
|
|
|
7
7
|
stringToBytes,
|
|
8
8
|
toHex,
|
|
9
9
|
} from "viem"
|
|
10
|
+
import { type AnyColumn, type Column, createColumn, isColumn } from "./column"
|
|
10
11
|
import { DECIMAL_UNIT, decimalToUnits, unitsToDecimal } from "./decimal"
|
|
11
12
|
import { InvalidValueError, MissingValueError, UntypedValueError } from "./errors"
|
|
12
13
|
import {
|
|
13
14
|
type AddrValue,
|
|
14
|
-
type AnyArkivValue,
|
|
15
15
|
type ArkivValue,
|
|
16
16
|
type BoolValue,
|
|
17
17
|
type Bytes32Value,
|
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
type KeyValue,
|
|
23
23
|
makeValue,
|
|
24
24
|
type StrValue,
|
|
25
|
+
type TypeTag,
|
|
25
26
|
type U64Value,
|
|
26
27
|
type U256Value,
|
|
27
28
|
} from "./types"
|
|
@@ -38,6 +39,11 @@ export const U256_MAX = 2n ** 256n - 1n
|
|
|
38
39
|
* The longest `str`, in UTF-8 bytes
|
|
39
40
|
*/
|
|
40
41
|
export const MAX_STRING_BYTES = 128
|
|
42
|
+
/**
|
|
43
|
+
* The longest `bytes` value, in bytes. The cap applies to each `bytes` attribute separately, but
|
|
44
|
+
* all the `bytes` values of one create or patch also share the `MAX_ENTITY_BYTES` budget.
|
|
45
|
+
*/
|
|
46
|
+
export const MAX_BYTES_LENGTH = 128 * 1024
|
|
41
47
|
|
|
42
48
|
/**
|
|
43
49
|
* Finds the first C0 control or DEL — the characters a `str` may not contain.
|
|
@@ -50,25 +56,32 @@ function findControlChar(value: string): { index: number; code: number } | undef
|
|
|
50
56
|
return undefined
|
|
51
57
|
}
|
|
52
58
|
|
|
59
|
+
/** A `bool` column, indexed for equality once `.indexed()`. */
|
|
60
|
+
export function bool(): Column<"bool", false, false, false>
|
|
53
61
|
/**
|
|
54
62
|
* A boolean.
|
|
55
63
|
*
|
|
56
|
-
*
|
|
57
|
-
* `{ flagged: bool(true) }` are the same
|
|
64
|
+
* This is the default type for a bare boolean, so `{ flagged: true }` and
|
|
65
|
+
* `{ flagged: bool(true) }` are the same value.
|
|
58
66
|
*
|
|
59
67
|
* @example
|
|
60
68
|
* bool(true) // { type: "bool", value: true }
|
|
61
69
|
*/
|
|
62
|
-
export function bool(value: boolean): BoolValue
|
|
70
|
+
export function bool(value: boolean): BoolValue
|
|
71
|
+
export function bool(...args: [] | [boolean]): BoolValue | Column<"bool", false, false, false> {
|
|
72
|
+
if (args.length === 0) return createColumn("bool", toTypedValue)
|
|
73
|
+
const [value] = args
|
|
63
74
|
if (typeof value !== "boolean") {
|
|
64
75
|
throw new InvalidValueError("bool", value, "not a boolean")
|
|
65
76
|
}
|
|
66
77
|
return makeValue("bool", value)
|
|
67
78
|
}
|
|
68
79
|
|
|
80
|
+
/** An `i32` column, indexed for equality and range queries once `.indexed()`. */
|
|
81
|
+
export function i32(): Column<"i32", false, false, false>
|
|
69
82
|
/**
|
|
70
|
-
* A 32-bit signed integer
|
|
71
|
-
*
|
|
83
|
+
* A 32-bit signed integer. This is the default type for a bare number, so `{ level: 10 }` and
|
|
84
|
+
* `{ level: i32(10) }` are the same value.
|
|
72
85
|
*
|
|
73
86
|
* @param value - An integer in [-2147483648, 2147483647].
|
|
74
87
|
* @throws {InvalidValueError} If the value is not an integer, or does not fit an `i32`.
|
|
@@ -77,7 +90,12 @@ export function bool(value: boolean): BoolValue {
|
|
|
77
90
|
* i32(10) // { type: "i32", value: 10 }
|
|
78
91
|
* i32(-42) // negative values are fine — i32 is the signed integer
|
|
79
92
|
*/
|
|
80
|
-
export function i32(value: number | bigint): I32Value
|
|
93
|
+
export function i32(value: number | bigint): I32Value
|
|
94
|
+
export function i32(
|
|
95
|
+
...args: [] | [number | bigint]
|
|
96
|
+
): I32Value | Column<"i32", false, false, false> {
|
|
97
|
+
if (args.length === 0) return createColumn("i32", toTypedValue)
|
|
98
|
+
const [value] = args
|
|
81
99
|
if (typeof value === "bigint") {
|
|
82
100
|
if (value > BigInt(I32_MAX) || value < BigInt(I32_MIN)) {
|
|
83
101
|
throw new InvalidValueError("i32", value, outOfI32Range(value >= 0n))
|
|
@@ -109,10 +127,11 @@ function outOfI32Range(nonNegative: boolean): string {
|
|
|
109
127
|
: `${range} — use dec(...) for values that need more range than an i32`
|
|
110
128
|
}
|
|
111
129
|
|
|
130
|
+
/** A `u256` column, indexed for equality and range queries once `.indexed()`. */
|
|
131
|
+
export function u256(): Column<"u256", false, false, false>
|
|
112
132
|
/**
|
|
113
|
-
* A 256-bit unsigned integer
|
|
114
|
-
*
|
|
115
|
-
* and block heights.
|
|
133
|
+
* A 256-bit unsigned integer. The type for token amounts and anything else that can outgrow 2⁶⁴ —
|
|
134
|
+
* reach for {@link u64} first for counters, timestamps and block heights.
|
|
116
135
|
*
|
|
117
136
|
* @param value - A non-negative integer, as a `bigint`, a safe `number`, or a decimal or `0x` hex
|
|
118
137
|
* string.
|
|
@@ -123,12 +142,18 @@ function outOfI32Range(nonNegative: boolean): string {
|
|
|
123
142
|
* u256(Date.now()) // safe integers are accepted
|
|
124
143
|
* u256("0xf4240") // hex, as it comes back over JSON-RPC
|
|
125
144
|
*/
|
|
126
|
-
export function u256(value: bigint | number | string): U256Value
|
|
127
|
-
|
|
145
|
+
export function u256(value: bigint | number | string): U256Value
|
|
146
|
+
export function u256(
|
|
147
|
+
...args: [] | [bigint | number | string]
|
|
148
|
+
): U256Value | Column<"u256", false, false, false> {
|
|
149
|
+
if (args.length === 0) return createColumn("u256", toTypedValue)
|
|
150
|
+
return makeValue("u256", toUnsigned("u256", args[0], U256_MAX))
|
|
128
151
|
}
|
|
129
152
|
|
|
153
|
+
/** A `u64` column, indexed for equality and range queries once `.indexed()`. */
|
|
154
|
+
export function u64(): Column<"u64", false, false, false>
|
|
130
155
|
/**
|
|
131
|
-
* A 64-bit unsigned integer
|
|
156
|
+
* A 64-bit unsigned integer.
|
|
132
157
|
*
|
|
133
158
|
* This is the width the protocol counts in: block heights, `$expiresAt` and the entity-nonce are
|
|
134
159
|
* all `u64`, so it is the type to reach for when a value has to line up with one of them. Use
|
|
@@ -143,8 +168,12 @@ export function u256(value: bigint | number | string): U256Value {
|
|
|
143
168
|
* u64(Date.now()) // safe integers are accepted
|
|
144
169
|
* u64("0x124f80") // hex, as it comes back over JSON-RPC
|
|
145
170
|
*/
|
|
146
|
-
export function u64(value: bigint | number | string): U64Value
|
|
147
|
-
|
|
171
|
+
export function u64(value: bigint | number | string): U64Value
|
|
172
|
+
export function u64(
|
|
173
|
+
...args: [] | [bigint | number | string]
|
|
174
|
+
): U64Value | Column<"u64", false, false, false> {
|
|
175
|
+
if (args.length === 0) return createColumn("u64", toTypedValue)
|
|
176
|
+
return makeValue("u64", toUnsigned("u64", args[0], U64_MAX))
|
|
148
177
|
}
|
|
149
178
|
|
|
150
179
|
/**
|
|
@@ -205,9 +234,10 @@ function toUnsignedBigint(tag: "u64" | "u256", value: bigint | number | string):
|
|
|
205
234
|
throw new InvalidValueError(tag, value, "not a bigint, number or string")
|
|
206
235
|
}
|
|
207
236
|
|
|
237
|
+
/** A `dec` column, indexed for equality and range queries once `.indexed()`. */
|
|
238
|
+
export function dec(): Column<"dec", false, false, false>
|
|
208
239
|
/**
|
|
209
|
-
* A signed fixed-point decimal with exactly 18 decimal places
|
|
210
|
-
* queries.
|
|
240
|
+
* A signed fixed-point decimal with exactly 18 decimal places.
|
|
211
241
|
*
|
|
212
242
|
* Pass fractional values as **strings**. A JavaScript number cannot represent most decimals
|
|
213
243
|
* exactly, so accepting `dec(0.1 + 0.2)` would quietly store `0.30000000000000004`; requiring the
|
|
@@ -221,9 +251,14 @@ function toUnsignedBigint(tag: "u64" | "u256", value: bigint | number | string):
|
|
|
221
251
|
* @example
|
|
222
252
|
* dec("3.5")
|
|
223
253
|
* dec("-0.000000000000000001") // the smallest step
|
|
224
|
-
* dec(5) // integers may be passed
|
|
254
|
+
* dec(5) // integers may be passed as a number
|
|
225
255
|
*/
|
|
226
|
-
export function dec(value: string | number | bigint): DecValue
|
|
256
|
+
export function dec(value: string | number | bigint): DecValue
|
|
257
|
+
export function dec(
|
|
258
|
+
...args: [] | [string | number | bigint]
|
|
259
|
+
): DecValue | Column<"dec", false, false, false> {
|
|
260
|
+
if (args.length === 0) return createColumn("dec", toTypedValue)
|
|
261
|
+
const [value] = args
|
|
227
262
|
if (typeof value === "string") {
|
|
228
263
|
return makeValue("dec", unitsToDecimal(decimalToUnits(value)))
|
|
229
264
|
}
|
|
@@ -266,8 +301,10 @@ export function decFromUnits(units: bigint): DecValue {
|
|
|
266
301
|
return makeValue("dec", unitsToDecimal(units))
|
|
267
302
|
}
|
|
268
303
|
|
|
304
|
+
/** A `str` column, indexed for equality and prefix (`STARTSWITH`) matching once `.indexed()`. */
|
|
305
|
+
export function str(): Column<"str", false, false, false>
|
|
269
306
|
/**
|
|
270
|
-
* A UTF-8 string of at most 128 bytes
|
|
307
|
+
* A UTF-8 string of at most 128 bytes.
|
|
271
308
|
*
|
|
272
309
|
* The limit is on **bytes**, not characters: a string of 128 emoji does not fit.
|
|
273
310
|
*
|
|
@@ -280,7 +317,10 @@ export function decFromUnits(units: bigint): DecValue {
|
|
|
280
317
|
* @example
|
|
281
318
|
* str("Bob")
|
|
282
319
|
*/
|
|
283
|
-
export function str(value: string): StrValue
|
|
320
|
+
export function str(value: string): StrValue
|
|
321
|
+
export function str(...args: [] | [string]): StrValue | Column<"str", false, false, false> {
|
|
322
|
+
if (args.length === 0) return createColumn("str", toTypedValue)
|
|
323
|
+
const [value] = args
|
|
284
324
|
if (typeof value !== "string") {
|
|
285
325
|
throw new InvalidValueError("str", value, "not a string")
|
|
286
326
|
}
|
|
@@ -293,7 +333,7 @@ export function str(value: string): StrValue {
|
|
|
293
333
|
`contains the control character U+${code} at index ${control.index}`,
|
|
294
334
|
"A str is written into a query as str('...'), whose only escape is a doubled quote — there " +
|
|
295
335
|
"is no spelling for a control character, so this value could be stored but never queried. " +
|
|
296
|
-
"Free-form text belongs in
|
|
336
|
+
"Free-form text belongs in a bytes column; keep a short, printable key here.",
|
|
297
337
|
)
|
|
298
338
|
}
|
|
299
339
|
const bytes = stringToBytes(value).length
|
|
@@ -302,14 +342,16 @@ export function str(value: string): StrValue {
|
|
|
302
342
|
"str",
|
|
303
343
|
value,
|
|
304
344
|
`${bytes} UTF-8 bytes exceeds the ${MAX_STRING_BYTES}-byte limit on a str`,
|
|
305
|
-
"Store the long form in
|
|
345
|
+
"Store the long form in a bytes column and keep a short, queryable key here.",
|
|
306
346
|
)
|
|
307
347
|
}
|
|
308
348
|
return makeValue("str", value)
|
|
309
349
|
}
|
|
310
350
|
|
|
351
|
+
/** An `addr` column, indexed for equality once `.indexed()`. */
|
|
352
|
+
export function addr(): Column<"addr", false, false, false>
|
|
311
353
|
/**
|
|
312
|
-
* A 20-byte Ethereum address
|
|
354
|
+
* A 20-byte Ethereum address.
|
|
313
355
|
*
|
|
314
356
|
* All-lowercase and all-uppercase input carries no checksum and is accepted as-is; mixed-case
|
|
315
357
|
* input is checked against its EIP-55 checksum, so a mistyped address is caught here rather than
|
|
@@ -320,7 +362,10 @@ export function str(value: string): StrValue {
|
|
|
320
362
|
* @example
|
|
321
363
|
* addr("0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045")
|
|
322
364
|
*/
|
|
323
|
-
export function addr(value: string): AddrValue
|
|
365
|
+
export function addr(value: string): AddrValue
|
|
366
|
+
export function addr(...args: [] | [string]): AddrValue | Column<"addr", false, false, false> {
|
|
367
|
+
if (args.length === 0) return createColumn("addr", toTypedValue)
|
|
368
|
+
const [value] = args
|
|
324
369
|
if (typeof value !== "string" || !/^0x[0-9a-fA-F]{40}$/.test(value)) {
|
|
325
370
|
throw new InvalidValueError("addr", value, "not a 20-byte 0x-prefixed hex address")
|
|
326
371
|
}
|
|
@@ -338,8 +383,10 @@ export function addr(value: string): AddrValue {
|
|
|
338
383
|
return makeValue("addr", checksumAddress(value as Address))
|
|
339
384
|
}
|
|
340
385
|
|
|
386
|
+
/** A `key` column — a reference to another entity — indexed for equality once `.indexed()`. */
|
|
387
|
+
export function key(): Column<"key", false, false, false>
|
|
341
388
|
/**
|
|
342
|
-
* A reference to another entity
|
|
389
|
+
* A reference to another entity.
|
|
343
390
|
*
|
|
344
391
|
* References are **weak**: nothing checks that the target exists when you write it, and a
|
|
345
392
|
* reference to a deleted or expired entity is allowed to dangle.
|
|
@@ -350,12 +397,18 @@ export function addr(value: string): AddrValue {
|
|
|
350
397
|
* @example
|
|
351
398
|
* key(parentEntityKey)
|
|
352
399
|
*/
|
|
353
|
-
export function key(value: Hex | Uint8Array): KeyValue
|
|
354
|
-
|
|
400
|
+
export function key(value: Hex | Uint8Array): KeyValue
|
|
401
|
+
export function key(
|
|
402
|
+
...args: [] | [Hex | Uint8Array]
|
|
403
|
+
): KeyValue | Column<"key", false, false, false> {
|
|
404
|
+
if (args.length === 0) return createColumn("key", toTypedValue)
|
|
405
|
+
return makeValue("key", asFixedHex("key", args[0], 32))
|
|
355
406
|
}
|
|
356
407
|
|
|
408
|
+
/** A `bytes32` column, indexed for equality once `.indexed()`. */
|
|
409
|
+
export function bytes32(): Column<"bytes32", false, false, false>
|
|
357
410
|
/**
|
|
358
|
-
* 32 opaque bytes — a hash, a digest, an identifier from another system
|
|
411
|
+
* 32 opaque bytes — a hash, a digest, an identifier from another system.
|
|
359
412
|
*
|
|
360
413
|
* Use {@link key} instead when the bytes are an Arkiv entity key: same wire width, but the
|
|
361
414
|
* distinct type makes the reference visible to readers and to the query language.
|
|
@@ -366,8 +419,50 @@ export function key(value: Hex | Uint8Array): KeyValue {
|
|
|
366
419
|
* @example
|
|
367
420
|
* bytes32(keccak256(toBytes("hello")))
|
|
368
421
|
*/
|
|
369
|
-
export function bytes32(value: Hex | Uint8Array): Bytes32Value
|
|
370
|
-
|
|
422
|
+
export function bytes32(value: Hex | Uint8Array): Bytes32Value
|
|
423
|
+
export function bytes32(
|
|
424
|
+
...args: [] | [Hex | Uint8Array]
|
|
425
|
+
): Bytes32Value | Column<"bytes32", false, false, false> {
|
|
426
|
+
if (args.length === 0) return createColumn("bytes32", toTypedValue)
|
|
427
|
+
return makeValue("bytes32", asFixedHex("bytes32", args[0], 32))
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
/**
|
|
431
|
+
* A `bytes` column — a blob. Bytes are stored but never indexed, so a bytes column has no
|
|
432
|
+
* `.indexed()`: it can be selected, not filtered on. A schema may have several, within the
|
|
433
|
+
* `MAX_ATTRIBUTES` cap on columns — but the `bytes` values of one create or patch share a single
|
|
434
|
+
* `MAX_ENTITY_BYTES` budget.
|
|
435
|
+
*/
|
|
436
|
+
export function bytes(): Column<"bytes", false, false, false>
|
|
437
|
+
/**
|
|
438
|
+
* Opaque bytes of any length up to 128 KiB — a document, an image, a serialized object. This is the
|
|
439
|
+
* default type for a bare `Uint8Array`, so `{ body: buf }` and `{ body: bytes(buf) }` are the same
|
|
440
|
+
* value.
|
|
441
|
+
*
|
|
442
|
+
* @param value - The bytes, raw or as `0x` hex. Hex is taken as the bytes it spells, never as text.
|
|
443
|
+
* @throws {InvalidValueError} If the value is not bytes or hex, or exceeds 128 KiB.
|
|
444
|
+
*
|
|
445
|
+
* @example
|
|
446
|
+
* bytes(new TextEncoder().encode("# Hello"))
|
|
447
|
+
* bytes("0xdeadbeef")
|
|
448
|
+
*/
|
|
449
|
+
export function bytes(value: Hex | Uint8Array): BytesValue
|
|
450
|
+
export function bytes(
|
|
451
|
+
...args: [] | [Hex | Uint8Array]
|
|
452
|
+
): BytesValue | Column<"bytes", false, false, false> {
|
|
453
|
+
if (args.length === 0) return createColumn("bytes", toTypedValue)
|
|
454
|
+
const [value] = args
|
|
455
|
+
const hex = asHex("bytes", value)
|
|
456
|
+
const length = (hex.length - 2) / 2
|
|
457
|
+
if (length > MAX_BYTES_LENGTH) {
|
|
458
|
+
throw new InvalidValueError(
|
|
459
|
+
"bytes",
|
|
460
|
+
`<${length} bytes>`,
|
|
461
|
+
`${length} bytes exceeds the ${MAX_BYTES_LENGTH}-byte limit on a bytes value`,
|
|
462
|
+
"Split it across several entities, or store the bulk elsewhere and keep a reference.",
|
|
463
|
+
)
|
|
464
|
+
}
|
|
465
|
+
return makeValue("bytes", hex)
|
|
371
466
|
}
|
|
372
467
|
|
|
373
468
|
/** Normalizes hex-or-bytes input of a known width into lowercase `0x` hex. */
|
|
@@ -378,39 +473,88 @@ function asFixedHex(tag: "key" | "bytes32", value: Hex | Uint8Array, size: numbe
|
|
|
378
473
|
}
|
|
379
474
|
return toHex(value)
|
|
380
475
|
}
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
}
|
|
384
|
-
if (value.length !== 2 + size * 2) {
|
|
476
|
+
const hex = asHex(tag, value)
|
|
477
|
+
if (hex.length !== 2 + size * 2) {
|
|
385
478
|
throw new InvalidValueError(
|
|
386
479
|
tag,
|
|
387
480
|
value,
|
|
388
|
-
`${(
|
|
481
|
+
`${(hex.length - 2) / 2} bytes, not exactly ${size} bytes`,
|
|
389
482
|
)
|
|
390
483
|
}
|
|
484
|
+
return hex
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
/** Normalizes hex-or-bytes input into lowercase `0x` hex of a whole number of bytes. */
|
|
488
|
+
function asHex(tag: "key" | "bytes32" | "bytes", value: Hex | Uint8Array): Hex {
|
|
489
|
+
if (value instanceof Uint8Array) return toHex(value)
|
|
490
|
+
if (typeof value !== "string" || !isHex(value, { strict: true })) {
|
|
491
|
+
throw new InvalidValueError(tag, value, "not 0x-prefixed hex")
|
|
492
|
+
}
|
|
493
|
+
if (value.length % 2 !== 0) {
|
|
494
|
+
throw new InvalidValueError(tag, value, "an odd number of hex digits, so not whole bytes")
|
|
495
|
+
}
|
|
391
496
|
return value.toLowerCase() as Hex
|
|
392
497
|
}
|
|
393
498
|
|
|
394
499
|
/**
|
|
395
500
|
* A JavaScript value accepted wherever a typed value is expected: either a tagged value, or one of
|
|
396
|
-
* the
|
|
501
|
+
* the five bare forms that map unambiguously onto a single type.
|
|
397
502
|
*
|
|
398
|
-
* | bare form
|
|
399
|
-
* |
|
|
400
|
-
* | `boolean`
|
|
401
|
-
* | `number`
|
|
402
|
-
* | `bigint`
|
|
403
|
-
* | `string`
|
|
503
|
+
* | bare form | type |
|
|
504
|
+
* | ------------ | ------- |
|
|
505
|
+
* | `boolean` | `bool` |
|
|
506
|
+
* | `number` | `i32` |
|
|
507
|
+
* | `bigint` | `u256` |
|
|
508
|
+
* | `string` | `str` |
|
|
509
|
+
* | `Uint8Array` | `bytes` |
|
|
404
510
|
*
|
|
405
511
|
* Anything else — a large number, a decimal, an address, an entity key — must name its type, which
|
|
406
512
|
* is what keeps `"0x1234..."` a string rather than silently becoming a key.
|
|
407
513
|
*
|
|
408
514
|
* These defaults apply identically when writing and when querying, so a value written bare and
|
|
409
|
-
* queried bare always agrees with itself. The mismatch to watch for is
|
|
410
|
-
*
|
|
411
|
-
*
|
|
515
|
+
* queried bare always agrees with itself. The mismatch to watch for is a bare value where its
|
|
516
|
+
* column has another type: a bare `bigint` is a `u256`, not a {@link u64}. Through a schema that is
|
|
517
|
+
* caught — a write throws `SchemaMismatchError` and a query throws `InvalidPredicateError` — but a
|
|
518
|
+
* query without a schema sends the predicate as written, and the engine, for which a type mismatch
|
|
519
|
+
* is not an error, simply matches nothing.
|
|
520
|
+
*/
|
|
521
|
+
export type ValueInput = ArkivValue | boolean | number | bigint | string | Uint8Array
|
|
522
|
+
|
|
523
|
+
/**
|
|
524
|
+
* The type a {@link ValueInput} resolves to — the tag of a tagged value, or the default of a bare
|
|
525
|
+
* form. `never` for anything else.
|
|
412
526
|
*/
|
|
413
|
-
export type
|
|
527
|
+
export type TagOf<input> = input extends { readonly type: infer tag extends TypeTag }
|
|
528
|
+
? tag
|
|
529
|
+
: input extends boolean
|
|
530
|
+
? "bool"
|
|
531
|
+
: input extends number
|
|
532
|
+
? "i32"
|
|
533
|
+
: input extends bigint
|
|
534
|
+
? "u256"
|
|
535
|
+
: input extends string
|
|
536
|
+
? "str"
|
|
537
|
+
: input extends Uint8Array
|
|
538
|
+
? "bytes"
|
|
539
|
+
: never
|
|
540
|
+
|
|
541
|
+
/**
|
|
542
|
+
* The bare JavaScript form that defaults to `tag`, where there is one — `boolean` for `bool`,
|
|
543
|
+
* `number` for `i32`, `bigint` for `u256`, `string` for `str`, `Uint8Array` for `bytes`. The other
|
|
544
|
+
* types — `u64`, `dec`, `bytes32`, `addr` and `key` — must be written tagged.
|
|
545
|
+
*/
|
|
546
|
+
export type BareOf<tag extends TypeTag> = {
|
|
547
|
+
bool: boolean
|
|
548
|
+
i32: number
|
|
549
|
+
u64: never
|
|
550
|
+
u256: bigint
|
|
551
|
+
dec: never
|
|
552
|
+
bytes32: never
|
|
553
|
+
bytes: Uint8Array
|
|
554
|
+
str: string
|
|
555
|
+
addr: never
|
|
556
|
+
key: never
|
|
557
|
+
}[tag]
|
|
414
558
|
|
|
415
559
|
/**
|
|
416
560
|
* Resolves a {@link ValueInput} to a typed value, applying the bare-form defaults above. Tagged
|
|
@@ -421,8 +565,8 @@ export type ValueInput = ArkivValue | boolean | number | bigint | string
|
|
|
421
565
|
*
|
|
422
566
|
* @throws {MissingValueError} If the input is `undefined` or `null` — an attribute is either set
|
|
423
567
|
* with a value and a type, or not set at all.
|
|
424
|
-
* @throws {UntypedValueError} If the input is not a tagged value or a bare boolean, number, bigint
|
|
425
|
-
* or
|
|
568
|
+
* @throws {UntypedValueError} If the input is not a tagged value or a bare boolean, number, bigint,
|
|
569
|
+
* string or `Uint8Array`.
|
|
426
570
|
* @throws {InvalidValueError} If a bare value does not fit the type it defaults to.
|
|
427
571
|
*
|
|
428
572
|
* @example
|
|
@@ -449,26 +593,61 @@ export function toValue(input: ValueInput, attributeName?: string): ArkivValue {
|
|
|
449
593
|
case "string":
|
|
450
594
|
return str(input)
|
|
451
595
|
default: {
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
//
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
throw new InvalidValueError(
|
|
459
|
-
"bytes",
|
|
460
|
-
candidate.value,
|
|
461
|
-
"system-only — bytes backs the entity payload and cannot be set as an attribute",
|
|
462
|
-
"Pass the bytes as the entity payload instead.",
|
|
463
|
-
)
|
|
464
|
-
}
|
|
465
|
-
return revalidate(candidate)
|
|
466
|
-
}
|
|
596
|
+
if (input instanceof Uint8Array) return bytes(input)
|
|
597
|
+
if (isArkivValue(input)) return revalidate(input)
|
|
598
|
+
// Widened: a column is not a ValueInput, but it is the one mix-up the dual-mode constructors
|
|
599
|
+
// invite — `str()` where `str("...")` was meant — so it is named rather than left untyped.
|
|
600
|
+
const candidate: unknown = input
|
|
601
|
+
if (isColumn(candidate)) throw columnGivenAsValue(candidate)
|
|
467
602
|
throw new UntypedValueError(input)
|
|
468
603
|
}
|
|
469
604
|
}
|
|
470
605
|
}
|
|
471
606
|
|
|
607
|
+
/**
|
|
608
|
+
* Resolves `input` to a value of type `tag` — a tagged value of that type, or whatever `tag`'s
|
|
609
|
+
* constructor accepts. What a column's `.default(...)` runs its argument through.
|
|
610
|
+
*
|
|
611
|
+
* @throws {InvalidValueError} If the input does not make a valid value of type `tag`.
|
|
612
|
+
*/
|
|
613
|
+
function toTypedValue(tag: TypeTag, input: unknown): ArkivValue {
|
|
614
|
+
if (isArkivValue(input)) {
|
|
615
|
+
const value = revalidate(input)
|
|
616
|
+
if (value.type !== tag) {
|
|
617
|
+
throw new InvalidValueError(tag, input, `a ${value.type} value, not a ${tag}`)
|
|
618
|
+
}
|
|
619
|
+
return value
|
|
620
|
+
}
|
|
621
|
+
if (isColumn(input)) throw columnGivenAsValue(input)
|
|
622
|
+
// Through the constructor, so every type accepts exactly what its constructor does.
|
|
623
|
+
const construct = CONSTRUCTORS[tag] as (value: unknown) => ArkivValue
|
|
624
|
+
return construct(input)
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
function columnGivenAsValue(column: AnyColumn): InvalidValueError {
|
|
628
|
+
return new InvalidValueError(
|
|
629
|
+
column.type,
|
|
630
|
+
`<${column.type} column>`,
|
|
631
|
+
"a column, not a value",
|
|
632
|
+
`Pass the value itself, e.g. ${column.type}(...) with an argument — ${column.type}() with ` +
|
|
633
|
+
"none declares a schema column.",
|
|
634
|
+
)
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
/** Each tag's constructor, called with a value. */
|
|
638
|
+
const CONSTRUCTORS: { readonly [tag in TypeTag]: (value: never) => ArkivValue } = {
|
|
639
|
+
bool,
|
|
640
|
+
i32,
|
|
641
|
+
u64,
|
|
642
|
+
u256,
|
|
643
|
+
dec,
|
|
644
|
+
bytes32,
|
|
645
|
+
bytes,
|
|
646
|
+
str,
|
|
647
|
+
addr,
|
|
648
|
+
key,
|
|
649
|
+
}
|
|
650
|
+
|
|
472
651
|
/**
|
|
473
652
|
* Re-runs a tagged value through its own constructor.
|
|
474
653
|
*
|
|
@@ -482,7 +661,7 @@ export function toValue(input: ValueInput, attributeName?: string): ArkivValue {
|
|
|
482
661
|
* has been range-, length- and format-checked" — true at runtime. For a value that really did come
|
|
483
662
|
* from a constructor it is a no-op that returns an equal value.
|
|
484
663
|
*/
|
|
485
|
-
function revalidate(value:
|
|
664
|
+
function revalidate(value: ArkivValue): ArkivValue {
|
|
486
665
|
switch (value.type) {
|
|
487
666
|
case "bool":
|
|
488
667
|
return bool(value.value)
|
|
@@ -496,6 +675,8 @@ function revalidate(value: Exclude<AnyArkivValue, BytesValue>): ArkivValue {
|
|
|
496
675
|
return dec(value.value)
|
|
497
676
|
case "bytes32":
|
|
498
677
|
return bytes32(value.value)
|
|
678
|
+
case "bytes":
|
|
679
|
+
return bytes(value.value)
|
|
499
680
|
case "str":
|
|
500
681
|
return str(value.value)
|
|
501
682
|
case "addr":
|
package/src/types/entity.test.ts
CHANGED
|
@@ -1,13 +1,9 @@
|
|
|
1
1
|
import { describe, expect, it } from "bun:test"
|
|
2
|
-
import { i32, str } from "../
|
|
2
|
+
import { i32, str } from "../schema"
|
|
3
3
|
import { Entity } from "./entity"
|
|
4
4
|
|
|
5
5
|
const KEY = `0x${"ab".repeat(32)}` as const
|
|
6
6
|
|
|
7
|
-
function withPayload(text: string): Entity {
|
|
8
|
-
return new Entity({ key: KEY, payload: new TextEncoder().encode(text) })
|
|
9
|
-
}
|
|
10
|
-
|
|
11
7
|
describe("Entity", () => {
|
|
12
8
|
it("carries only the fields it was built with", () => {
|
|
13
9
|
const entity = new Entity({
|
|
@@ -20,7 +16,6 @@ describe("Entity", () => {
|
|
|
20
16
|
expect(entity.attributes?.level.value).toBe(10)
|
|
21
17
|
// Not selected is not the same as empty: nothing here stands in for data that was not asked for.
|
|
22
18
|
expect(entity.owner).toBeUndefined()
|
|
23
|
-
expect(entity.payload).toBeUndefined()
|
|
24
19
|
expect(entity.attributeSchema).toBeUndefined()
|
|
25
20
|
})
|
|
26
21
|
|
|
@@ -28,41 +23,3 @@ describe("Entity", () => {
|
|
|
28
23
|
expect(new Entity().key).toBeUndefined()
|
|
29
24
|
})
|
|
30
25
|
})
|
|
31
|
-
|
|
32
|
-
describe("toText", () => {
|
|
33
|
-
it("decodes the payload as UTF-8", () => {
|
|
34
|
-
expect(withPayload("hello world").toText()).toBe("hello world")
|
|
35
|
-
expect(withPayload("").toText()).toBe("")
|
|
36
|
-
})
|
|
37
|
-
|
|
38
|
-
it("says how to ask for the payload when it was not selected", () => {
|
|
39
|
-
expect(() => new Entity({ key: KEY }).toText()).toThrow(/select\(\{ payload: true \}\)/)
|
|
40
|
-
})
|
|
41
|
-
})
|
|
42
|
-
|
|
43
|
-
describe("toJson", () => {
|
|
44
|
-
it("parses objects and arrays", () => {
|
|
45
|
-
expect(withPayload('{"a":1,"b":[2,3]}').toJson()).toEqual({ a: 1, b: [2, 3] })
|
|
46
|
-
expect(withPayload("[1,2,3]").toJson()).toEqual([1, 2, 3])
|
|
47
|
-
})
|
|
48
|
-
|
|
49
|
-
it("throws on an empty payload rather than returning undefined", () => {
|
|
50
|
-
expect(() => withPayload("").toJson()).toThrow(/empty payload/)
|
|
51
|
-
})
|
|
52
|
-
|
|
53
|
-
it("keeps the parse failure as the cause", () => {
|
|
54
|
-
let caught: unknown
|
|
55
|
-
try {
|
|
56
|
-
withPayload("not json").toJson()
|
|
57
|
-
} catch (error) {
|
|
58
|
-
caught = error
|
|
59
|
-
}
|
|
60
|
-
expect(caught).toBeInstanceOf(Error)
|
|
61
|
-
expect((caught as Error).message).toMatch(/Failed to parse entity payload as JSON/)
|
|
62
|
-
expect((caught as Error).cause).toBeInstanceOf(Error)
|
|
63
|
-
})
|
|
64
|
-
|
|
65
|
-
it("reports the missing payload, not a JSON error", () => {
|
|
66
|
-
expect(() => new Entity({ key: KEY }).toJson()).toThrow(/did not select it/)
|
|
67
|
-
})
|
|
68
|
-
})
|