@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
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { InvalidValueError } from "./errors"
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Decimal places a `dec` value is scaled by. Fixed by the protocol, so any two decimals are
|
|
5
|
+
* directly comparable — and range-indexable — as plain integers.
|
|
6
|
+
*/
|
|
7
|
+
export const DECIMAL_SCALE = 18
|
|
8
|
+
|
|
9
|
+
/** 10 ** {@link DECIMAL_SCALE} — the scaling factor between a `dec` and its integer units. */
|
|
10
|
+
export const DECIMAL_UNIT = 10n ** BigInt(DECIMAL_SCALE)
|
|
11
|
+
|
|
12
|
+
/** The widest scaled value an `int256` holds. */
|
|
13
|
+
const INT256_MAX = 2n ** 255n - 1n
|
|
14
|
+
/** The narrowest scaled value an `int256` holds. */
|
|
15
|
+
const INT256_MIN = -(2n ** 255n)
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* A decimal literal: an optional sign, at least one integer digit, and an optional fraction of at
|
|
19
|
+
* most {@link DECIMAL_SCALE} digits. Exponents are not part of the grammar — `1e18` is a parse
|
|
20
|
+
* error rather than a silent reinterpretation.
|
|
21
|
+
*/
|
|
22
|
+
const DECIMAL_RE = /^([+-]?)(\d+)(?:\.(\d+))?$/
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Parses a decimal literal into its scaled integer units.
|
|
26
|
+
*
|
|
27
|
+
* Excess fractional precision is an **error, never a rounding**: the protocol stores exactly 18
|
|
28
|
+
* decimal places, and silently dropping the 19th digit would make a stored value differ from the
|
|
29
|
+
* one that was written.
|
|
30
|
+
*
|
|
31
|
+
* @param input - The decimal literal, e.g. `"3.5"` or `"-0.000001"`.
|
|
32
|
+
* @returns The value scaled by 10 ** 18.
|
|
33
|
+
* @throws {InvalidValueError} If the literal is malformed, too precise, or out of `int256` range.
|
|
34
|
+
*/
|
|
35
|
+
export function decimalToUnits(input: string): bigint {
|
|
36
|
+
const match = DECIMAL_RE.exec(input)
|
|
37
|
+
if (!match) {
|
|
38
|
+
throw new InvalidValueError(
|
|
39
|
+
"dec",
|
|
40
|
+
input,
|
|
41
|
+
"not a decimal literal",
|
|
42
|
+
'Write it as sign, digits, and an optional "." with up to 18 fractional digits — e.g. ' +
|
|
43
|
+
'dec("3.5") or dec("-0.25"). Exponents such as "1e18" are not accepted.',
|
|
44
|
+
)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const [, sign, whole, fraction = ""] = match
|
|
48
|
+
if (fraction.length > DECIMAL_SCALE) {
|
|
49
|
+
throw new InvalidValueError(
|
|
50
|
+
"dec",
|
|
51
|
+
input,
|
|
52
|
+
`${fraction.length} fractional digits exceeds the ${DECIMAL_SCALE} the protocol stores`,
|
|
53
|
+
"Round it yourself before storing — a dec is never rounded for you, so that what you " +
|
|
54
|
+
"write is exactly what is stored.",
|
|
55
|
+
)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const digits = BigInt(whole + fraction.padEnd(DECIMAL_SCALE, "0"))
|
|
59
|
+
const units = sign === "-" ? -digits : digits
|
|
60
|
+
if (units > INT256_MAX || units < INT256_MIN) {
|
|
61
|
+
throw new InvalidValueError("dec", input, "exceeds the int256 range a dec is stored in")
|
|
62
|
+
}
|
|
63
|
+
return units
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Renders scaled integer units back as a canonical decimal string: no leading zeros, no trailing
|
|
68
|
+
* fractional zeros, no `-0`. Parsing the result yields the same units, so this is the inverse of
|
|
69
|
+
* {@link decimalToUnits} on every value it accepts.
|
|
70
|
+
*
|
|
71
|
+
* @param units - The value scaled by 10 ** 18.
|
|
72
|
+
* @returns The canonical decimal string, e.g. `"3.5"`.
|
|
73
|
+
* @throws {InvalidValueError} If the units are outside the `int256` range.
|
|
74
|
+
*/
|
|
75
|
+
export function unitsToDecimal(units: bigint): string {
|
|
76
|
+
if (units > INT256_MAX || units < INT256_MIN) {
|
|
77
|
+
throw new InvalidValueError("dec", units, "exceeds the int256 range a dec is stored in")
|
|
78
|
+
}
|
|
79
|
+
const negative = units < 0n
|
|
80
|
+
const magnitude = negative ? -units : units
|
|
81
|
+
const whole = magnitude / DECIMAL_UNIT
|
|
82
|
+
const fraction = (magnitude % DECIMAL_UNIT).toString().padStart(DECIMAL_SCALE, "0")
|
|
83
|
+
const trimmed = fraction.replace(/0+$/, "")
|
|
84
|
+
const sign = negative ? "-" : ""
|
|
85
|
+
return trimmed ? `${sign}${whole}.${trimmed}` : `${sign}${whole}`
|
|
86
|
+
}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import type { TypeTag } from "./types"
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* A value was rejected by its tagged constructor — out of range, wrong length, bad checksum, too
|
|
5
|
+
* much precision, and so on.
|
|
6
|
+
*
|
|
7
|
+
* The message names the offending value and, where there is one, the type that *would* hold it, so
|
|
8
|
+
* the fix is in the error rather than in the docs.
|
|
9
|
+
*/
|
|
10
|
+
export class InvalidValueError extends Error {
|
|
11
|
+
/** The type tag whose constructor rejected the value. */
|
|
12
|
+
readonly tag: TypeTag
|
|
13
|
+
/** The rejected input, as given. */
|
|
14
|
+
readonly input: unknown
|
|
15
|
+
|
|
16
|
+
constructor(tag: TypeTag, input: unknown, reason: string, hint?: string) {
|
|
17
|
+
super(`Invalid ${tag} value ${describe(input)}: ${reason}.${hint ? ` ${hint}` : ""}`)
|
|
18
|
+
this.name = "InvalidValueError"
|
|
19
|
+
this.tag = tag
|
|
20
|
+
this.input = input
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* A bare JavaScript value could not be given a type. Raised when an attribute or predicate is
|
|
26
|
+
* handed something that is neither a tagged value nor one of the four bare forms
|
|
27
|
+
* (`boolean`, `number`, `bigint`, `string`).
|
|
28
|
+
*/
|
|
29
|
+
export class UntypedValueError extends Error {
|
|
30
|
+
readonly input: unknown
|
|
31
|
+
|
|
32
|
+
constructor(input: unknown) {
|
|
33
|
+
super(
|
|
34
|
+
`Cannot infer an Arkiv type for ${describe(input)}. ` +
|
|
35
|
+
"Attribute values must be a tagged value (i32, u256, dec, str, addr, key, bytes32, bool) " +
|
|
36
|
+
"or a bare boolean, number, bigint or string.",
|
|
37
|
+
)
|
|
38
|
+
this.name = "UntypedValueError"
|
|
39
|
+
this.input = input
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* An attribute was given `undefined` or `null` as its value.
|
|
45
|
+
*
|
|
46
|
+
* There is no such thing as an attribute with no value: an attribute either exists with a type, or
|
|
47
|
+
* is not set. The fix is always to leave the name out of the map rather than to write an empty
|
|
48
|
+
* value, so this is called out separately from {@link UntypedValueError} — the remedy is different.
|
|
49
|
+
*/
|
|
50
|
+
export class MissingValueError extends Error {
|
|
51
|
+
/** The attribute whose value was missing, when raised from an attribute map. */
|
|
52
|
+
readonly attributeName: string | undefined
|
|
53
|
+
|
|
54
|
+
constructor(input: null | undefined, attributeName?: string) {
|
|
55
|
+
const subject = attributeName ? `attribute "${attributeName}"` : "attribute value"
|
|
56
|
+
super(
|
|
57
|
+
`The ${subject} is ${input === null ? "null" : "undefined"}. An attribute is either set ` +
|
|
58
|
+
"with a value and a type or not set at all, so there is nothing to write here. " +
|
|
59
|
+
(attributeName
|
|
60
|
+
? `Omit "${attributeName}" from the attributes instead — e.g. build the map ` +
|
|
61
|
+
"conditionally, or drop the key when the value is absent."
|
|
62
|
+
: "Omit the attribute instead of giving it an empty value."),
|
|
63
|
+
)
|
|
64
|
+
this.name = "MissingValueError"
|
|
65
|
+
this.attributeName = attributeName
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** An attribute name violated the on-chain name grammar. */
|
|
70
|
+
export class InvalidAttributeNameError extends Error {
|
|
71
|
+
readonly attributeName: string
|
|
72
|
+
|
|
73
|
+
constructor(attributeName: string, reason: string) {
|
|
74
|
+
super(
|
|
75
|
+
`Invalid attribute name "${attributeName}": ${reason}. Names must be 1-32 bytes, start with ` +
|
|
76
|
+
'a letter, and contain only A-Z, a-z, 0-9, ".", "-" or "_". Names are case-sensitive.',
|
|
77
|
+
)
|
|
78
|
+
this.name = "InvalidAttributeNameError"
|
|
79
|
+
this.attributeName = attributeName
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* One patch tried to both write and remove the same attribute.
|
|
85
|
+
*/
|
|
86
|
+
export class ConflictingMutationError extends Error {
|
|
87
|
+
readonly attributeName: string
|
|
88
|
+
|
|
89
|
+
constructor(attributeName: string) {
|
|
90
|
+
super(
|
|
91
|
+
`The patch both sets and unsets "${attributeName}". A patch applies one mutation per ` +
|
|
92
|
+
'attribute, so say which you mean: drop it from "unset" to write the value, or from ' +
|
|
93
|
+
'"set" to remove the attribute.',
|
|
94
|
+
)
|
|
95
|
+
this.name = "ConflictingMutationError"
|
|
96
|
+
this.attributeName = attributeName
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/** More attributes were supplied than an entity operation can carry. */
|
|
101
|
+
export class TooManyAttributesError extends Error {
|
|
102
|
+
constructor(count: number, max: number) {
|
|
103
|
+
super(`An entity may carry at most ${max} attributes, got ${count}.`)
|
|
104
|
+
this.name = "TooManyAttributesError"
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/** The node sent back an attribute this SDK cannot decode. */
|
|
109
|
+
export class UnknownAttributeTypeError extends Error {
|
|
110
|
+
/** The type as the node named it — a tag over JSON-RPC, a typeId on the wire. */
|
|
111
|
+
readonly type: string | number
|
|
112
|
+
|
|
113
|
+
constructor(type: string | number) {
|
|
114
|
+
super(
|
|
115
|
+
`Unknown attribute type ${describe(type)}. The node is using a type this SDK version does ` +
|
|
116
|
+
"not know about — upgrade @arkiv-network/sdk.",
|
|
117
|
+
)
|
|
118
|
+
this.name = "UnknownAttributeTypeError"
|
|
119
|
+
this.type = type
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** Renders an arbitrary input for an error message, without ever throwing. */
|
|
124
|
+
function describe(input: unknown): string {
|
|
125
|
+
switch (typeof input) {
|
|
126
|
+
case "string":
|
|
127
|
+
return JSON.stringify(input.length > 64 ? `${input.slice(0, 61)}...` : input)
|
|
128
|
+
case "bigint":
|
|
129
|
+
return `${input}n`
|
|
130
|
+
case "object":
|
|
131
|
+
return input === null ? "null" : safeJson(input)
|
|
132
|
+
case "undefined":
|
|
133
|
+
return "undefined"
|
|
134
|
+
default:
|
|
135
|
+
return String(input)
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function safeJson(input: object): string {
|
|
140
|
+
try {
|
|
141
|
+
return JSON.stringify(input) ?? String(input)
|
|
142
|
+
} catch {
|
|
143
|
+
return Object.prototype.toString.call(input)
|
|
144
|
+
}
|
|
145
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
export type { AttributeInputs, AttributeSchema, Attributes } from "./attributes"
|
|
2
|
+
export { MAX_ATTRIBUTES, MAX_PAYLOAD_BYTES } from "./attributes"
|
|
3
|
+
export { DECIMAL_SCALE, DECIMAL_UNIT } from "./decimal"
|
|
4
|
+
export {
|
|
5
|
+
ConflictingMutationError,
|
|
6
|
+
InvalidAttributeNameError,
|
|
7
|
+
InvalidValueError,
|
|
8
|
+
MissingValueError,
|
|
9
|
+
TooManyAttributesError,
|
|
10
|
+
UnknownAttributeTypeError,
|
|
11
|
+
UntypedValueError,
|
|
12
|
+
} from "./errors"
|
|
13
|
+
export { isValidAttributeName, MAX_NAME_BYTES, validateAttributeName } from "./names"
|
|
14
|
+
export type {
|
|
15
|
+
AddrValue,
|
|
16
|
+
AnyArkivValue,
|
|
17
|
+
ArkivValue,
|
|
18
|
+
BoolValue,
|
|
19
|
+
Bytes32Value,
|
|
20
|
+
BytesValue,
|
|
21
|
+
DecValue,
|
|
22
|
+
I32Value,
|
|
23
|
+
KeyValue,
|
|
24
|
+
StrValue,
|
|
25
|
+
TypeTag,
|
|
26
|
+
U64Value,
|
|
27
|
+
U256Value,
|
|
28
|
+
UserTypeTag,
|
|
29
|
+
} from "./types"
|
|
30
|
+
export { isArkivValue } from "./types"
|
|
31
|
+
export type { ValueInput } from "./values"
|
|
32
|
+
export {
|
|
33
|
+
addr,
|
|
34
|
+
bool,
|
|
35
|
+
bytes32,
|
|
36
|
+
dec,
|
|
37
|
+
decFromUnits,
|
|
38
|
+
decUnits,
|
|
39
|
+
I32_MAX,
|
|
40
|
+
I32_MIN,
|
|
41
|
+
i32,
|
|
42
|
+
key,
|
|
43
|
+
MAX_STRING_BYTES,
|
|
44
|
+
str,
|
|
45
|
+
toValue,
|
|
46
|
+
U64_MAX,
|
|
47
|
+
U256_MAX,
|
|
48
|
+
u64,
|
|
49
|
+
u256,
|
|
50
|
+
} from "./values"
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { InvalidAttributeNameError } from "./errors"
|
|
2
|
+
|
|
3
|
+
/** The longest attribute name, in bytes — the width of the on-chain `Ident32` name cell. */
|
|
4
|
+
export const MAX_NAME_BYTES = 32
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* The attribute-name grammar (`Ident32`): a leading letter, then letters, digits, `.`, `-` or `_`.
|
|
8
|
+
*
|
|
9
|
+
* Names are **case-sensitive** — `Level` and `level` are two different attributes.
|
|
10
|
+
*
|
|
11
|
+
* The charset is ASCII-only, so a name's length in characters is also its length in bytes.
|
|
12
|
+
*/
|
|
13
|
+
const NAME_RE = /^[A-Za-z][A-Za-z0-9._-]*$/
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Words the query language reserves. They are rejected as attribute names because an attribute
|
|
17
|
+
* named `not` or `str` could be written but never queried — the parser would read the name as an
|
|
18
|
+
* operator or a type tag.
|
|
19
|
+
*
|
|
20
|
+
* Matched **case-insensitively**: query keywords are case-insensitive, so `NOT` and `not` are the
|
|
21
|
+
* same operator, and the type tags are held to the same rule so that what is reserved does not
|
|
22
|
+
* depend on how you happen to capitalise it.
|
|
23
|
+
*/
|
|
24
|
+
const RESERVED = new Set([
|
|
25
|
+
// operators and keywords
|
|
26
|
+
"and",
|
|
27
|
+
"or",
|
|
28
|
+
"not",
|
|
29
|
+
"true",
|
|
30
|
+
"false",
|
|
31
|
+
"startswith",
|
|
32
|
+
"exists",
|
|
33
|
+
"typeof",
|
|
34
|
+
// type tags
|
|
35
|
+
"bool",
|
|
36
|
+
"i32",
|
|
37
|
+
"u64",
|
|
38
|
+
"u256",
|
|
39
|
+
"dec",
|
|
40
|
+
"bytes32",
|
|
41
|
+
"bytes",
|
|
42
|
+
"str",
|
|
43
|
+
"addr",
|
|
44
|
+
"key",
|
|
45
|
+
])
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Validates an attribute name against the engine's `Ident32` grammar and the query language's
|
|
49
|
+
* reserved words.
|
|
50
|
+
*
|
|
51
|
+
* @param name - The attribute name.
|
|
52
|
+
* @throws {InvalidAttributeNameError} If the name is empty, too long, uses characters outside the
|
|
53
|
+
* grammar, or collides with a reserved word.
|
|
54
|
+
*/
|
|
55
|
+
export function validateAttributeName(name: string): void {
|
|
56
|
+
if (typeof name !== "string" || name.length === 0) {
|
|
57
|
+
throw new InvalidAttributeNameError(String(name), "the name is empty")
|
|
58
|
+
}
|
|
59
|
+
if (name.startsWith("$")) {
|
|
60
|
+
throw new InvalidAttributeNameError(
|
|
61
|
+
name,
|
|
62
|
+
'"$" is reserved for system attributes, which the engine sets and you cannot write',
|
|
63
|
+
)
|
|
64
|
+
}
|
|
65
|
+
if (!NAME_RE.test(name)) {
|
|
66
|
+
const chars = [...name]
|
|
67
|
+
const at = chars.findIndex(
|
|
68
|
+
(char, index) => !(index === 0 ? /[A-Za-z]/ : /[A-Za-z0-9._-]/).test(char),
|
|
69
|
+
)
|
|
70
|
+
throw new InvalidAttributeNameError(
|
|
71
|
+
name,
|
|
72
|
+
at === 0
|
|
73
|
+
? `it starts with "${chars[0]}" rather than a letter`
|
|
74
|
+
: `it contains "${chars[at]}" at position ${at}`,
|
|
75
|
+
)
|
|
76
|
+
}
|
|
77
|
+
if (name.length > MAX_NAME_BYTES) {
|
|
78
|
+
throw new InvalidAttributeNameError(name, `the name is ${name.length} bytes long`)
|
|
79
|
+
}
|
|
80
|
+
// `-` is a legal name character and `--` opens a comment that runs to the end of the line, so a
|
|
81
|
+
// name containing one renders a query whose whole remainder is discarded
|
|
82
|
+
if (name.includes("--")) {
|
|
83
|
+
throw new InvalidAttributeNameError(
|
|
84
|
+
name,
|
|
85
|
+
'"--" opens a comment in the query language, so everything after it in a query would be ' +
|
|
86
|
+
"discarded — an attribute with this name could be written but never safely queried",
|
|
87
|
+
)
|
|
88
|
+
}
|
|
89
|
+
if (RESERVED.has(name.toLowerCase())) {
|
|
90
|
+
throw new InvalidAttributeNameError(
|
|
91
|
+
name,
|
|
92
|
+
`"${name}" is reserved by the query language, so an attribute with this name could never ` +
|
|
93
|
+
"be queried",
|
|
94
|
+
)
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** Whether `name` is a valid attribute name — the non-throwing form of {@link validateAttributeName}. */
|
|
99
|
+
export function isValidAttributeName(name: string): boolean {
|
|
100
|
+
try {
|
|
101
|
+
validateAttributeName(name)
|
|
102
|
+
return true
|
|
103
|
+
} catch {
|
|
104
|
+
return false
|
|
105
|
+
}
|
|
106
|
+
}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import type { Address, Hex } from "viem"
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The protocol's attribute type vocabulary, mapping each short **tag** (the name used by the
|
|
5
|
+
* client, the query language and this SDK) to its **typeId** (the byte carried on the wire and in
|
|
6
|
+
* the entity record).
|
|
7
|
+
*
|
|
8
|
+
* The typeIds are consensus-critical: they are written into entity records and mixed into index
|
|
9
|
+
* keys, so this table must never drift from the node's `AttributeType`.
|
|
10
|
+
*
|
|
11
|
+
* | tag | typeId | ABI wire type | indexing |
|
|
12
|
+
* | --------- | ------ | ------------------- | ----------------- |
|
|
13
|
+
* | `bool` | 1 | `bool` | equality |
|
|
14
|
+
* | `i32` | 2 | `int32` | equality + range |
|
|
15
|
+
* | `u64` | 3 | `uint64` | equality + range |
|
|
16
|
+
* | `u256` | 4 | `uint256` | equality + range |
|
|
17
|
+
* | `dec` | 5 | `int256` (18 dp) | equality + range |
|
|
18
|
+
* | `bytes32` | 6 | `bytes32` | equality |
|
|
19
|
+
* | `bytes` | 7 | `bytes` | none — system-only |
|
|
20
|
+
* | `str` | 8 | `string` (<= 128 B) | equality + prefix |
|
|
21
|
+
* | `addr` | 9 | `address` | equality |
|
|
22
|
+
* | `key` | 10 | `bytes32` | equality |
|
|
23
|
+
*/
|
|
24
|
+
export const TYPE_IDS = {
|
|
25
|
+
bool: 1,
|
|
26
|
+
i32: 2,
|
|
27
|
+
u64: 3,
|
|
28
|
+
u256: 4,
|
|
29
|
+
dec: 5,
|
|
30
|
+
bytes32: 6,
|
|
31
|
+
bytes: 7,
|
|
32
|
+
str: 8,
|
|
33
|
+
addr: 9,
|
|
34
|
+
key: 10,
|
|
35
|
+
} as const
|
|
36
|
+
|
|
37
|
+
/** Every type tag, including the system-only `bytes`. */
|
|
38
|
+
export type TypeTag = keyof typeof TYPE_IDS
|
|
39
|
+
|
|
40
|
+
/** Every typeId, including the system-only `7` (`bytes`). */
|
|
41
|
+
export type TypeId = (typeof TYPE_IDS)[TypeTag]
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* The tags a client may set on an attribute. `bytes` is excluded: it backs the system `$payload`
|
|
45
|
+
* cell, which travels in its own operation field and is never carried in an attribute array.
|
|
46
|
+
*/
|
|
47
|
+
export type UserTypeTag = Exclude<TypeTag, "bytes">
|
|
48
|
+
|
|
49
|
+
/** Whether a client may set an attribute of this type — everything except `bytes`. */
|
|
50
|
+
export function isUserSettable(tag: TypeTag): tag is UserTypeTag {
|
|
51
|
+
return tag !== "bytes"
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Marks a value as having passed its constructor's validation. The symbol is module-private, so a
|
|
56
|
+
* hand-written object literal is not assignable to {@link ArkivValue} — every value crossing the
|
|
57
|
+
* wire has been range-, length- and format-checked by {@link i32}, {@link u256}, {@link dec}, and
|
|
58
|
+
* friends.
|
|
59
|
+
*/
|
|
60
|
+
declare const validated: unique symbol
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* A typed attribute value: the tag it was created with, plus the JavaScript representation of that
|
|
64
|
+
* type. Constructed only by the tagged constructors in `@arkiv-network/sdk/attr`.
|
|
65
|
+
*
|
|
66
|
+
* @typeParam tag - The type tag, which also discriminates the {@link ArkivValue} union.
|
|
67
|
+
* @typeParam value - The decoded JavaScript representation.
|
|
68
|
+
*/
|
|
69
|
+
export type Value<tag extends TypeTag, value> = {
|
|
70
|
+
readonly type: tag
|
|
71
|
+
readonly value: value
|
|
72
|
+
readonly [validated]: tag
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** A boolean. */
|
|
76
|
+
export type BoolValue = Value<"bool", boolean>
|
|
77
|
+
/** A 32-bit signed integer — the default for an untagged number. */
|
|
78
|
+
export type I32Value = Value<"i32", number>
|
|
79
|
+
/**
|
|
80
|
+
* A 64-bit unsigned integer — block heights, the
|
|
81
|
+
* entity-nonce and `$expiresAt` are all `u64` */
|
|
82
|
+
export type U64Value = Value<"u64", bigint>
|
|
83
|
+
/** A 256-bit unsigned integer. */
|
|
84
|
+
export type U256Value = Value<"u256", bigint>
|
|
85
|
+
/**
|
|
86
|
+
* A fixed-point decimal with 18 decimal places, held as its canonical decimal string (`"3.5"`).
|
|
87
|
+
* Use {@link decUnits} for the exact scaled integer when you need to do arithmetic.
|
|
88
|
+
*/
|
|
89
|
+
export type DecValue = Value<"dec", string>
|
|
90
|
+
/** 32 opaque bytes, as lowercase `0x` hex. */
|
|
91
|
+
export type Bytes32Value = Value<"bytes32", Hex>
|
|
92
|
+
/** A UTF-8 string of at most 128 bytes. */
|
|
93
|
+
export type StrValue = Value<"str", string>
|
|
94
|
+
/** A 20-byte Ethereum address, as EIP-55 checksummed hex. */
|
|
95
|
+
export type AddrValue = Value<"addr", Address>
|
|
96
|
+
/** A reference to another entity. Weak by spec: dangling references are permitted. */
|
|
97
|
+
export type KeyValue = Value<"key", Hex>
|
|
98
|
+
/**
|
|
99
|
+
* Opaque variable-length bytes, as lowercase `0x` hex. System-only — it backs `$payload` and is
|
|
100
|
+
* never settable as an attribute, so it is produced by decoding but has no constructor.
|
|
101
|
+
*/
|
|
102
|
+
export type BytesValue = Value<"bytes", Hex>
|
|
103
|
+
|
|
104
|
+
/** Any value a client may set on an attribute. */
|
|
105
|
+
export type ArkivValue =
|
|
106
|
+
| BoolValue
|
|
107
|
+
| I32Value
|
|
108
|
+
| U64Value
|
|
109
|
+
| U256Value
|
|
110
|
+
| DecValue
|
|
111
|
+
| Bytes32Value
|
|
112
|
+
| StrValue
|
|
113
|
+
| AddrValue
|
|
114
|
+
| KeyValue
|
|
115
|
+
|
|
116
|
+
/** Any value that can come back from the node, including the system-only `bytes`. */
|
|
117
|
+
export type AnyArkivValue = ArkivValue | BytesValue
|
|
118
|
+
|
|
119
|
+
/** The value type for a given tag — e.g. `ValueOf<"u256">` is {@link U256Value}. */
|
|
120
|
+
export type ValueOf<tag extends TypeTag> = Extract<AnyArkivValue, { type: tag }>
|
|
121
|
+
|
|
122
|
+
/** The JavaScript representation carried by a value — e.g. `Decoded<U256Value>` is `bigint`. */
|
|
123
|
+
export type Decoded<value extends AnyArkivValue> = value["value"]
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Builds a validated value. Module-internal: the constructors in `values.ts` call this **after**
|
|
127
|
+
* validating, which is what makes the {@link validated} brand meaningful.
|
|
128
|
+
*/
|
|
129
|
+
export function makeValue<tag extends TypeTag>(
|
|
130
|
+
type: tag,
|
|
131
|
+
value: Decoded<ValueOf<tag>>,
|
|
132
|
+
): ValueOf<tag> {
|
|
133
|
+
return { type, value } as unknown as ValueOf<tag>
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Whether `input` has the *shape* of an {@link ArkivValue} — a known type tag and a value.
|
|
138
|
+
*/
|
|
139
|
+
export function isArkivValue(input: unknown): input is AnyArkivValue {
|
|
140
|
+
return (
|
|
141
|
+
typeof input === "object" &&
|
|
142
|
+
input !== null &&
|
|
143
|
+
"type" in input &&
|
|
144
|
+
"value" in input &&
|
|
145
|
+
typeof (input as { type: unknown }).type === "string" &&
|
|
146
|
+
// `Object.hasOwn`, not `in`: `in` walks the prototype chain, so `{ type: "constructor" }` would
|
|
147
|
+
// be accepted as a typed value and then decode to nothing.
|
|
148
|
+
Object.hasOwn(TYPE_IDS, (input as { type: string }).type)
|
|
149
|
+
)
|
|
150
|
+
}
|