@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.
Files changed (210) hide show
  1. package/README.md +29 -16
  2. package/dist/attr/index.cjs +34 -0
  3. package/dist/attr/index.d.cts +2 -0
  4. package/dist/attr/index.d.ts +2 -0
  5. package/dist/attr/index.js +3 -0
  6. package/dist/attr-BDZ-APg5.js +809 -0
  7. package/dist/attr-BDZ-APg5.js.map +1 -0
  8. package/dist/attr-BvxHN99W.cjs +1029 -0
  9. package/dist/chains/index.cjs +2 -3
  10. package/dist/chains/index.d.cts +39 -77
  11. package/dist/chains/index.d.cts.map +1 -1
  12. package/dist/chains/index.d.ts +22 -60
  13. package/dist/chains/index.d.ts.map +1 -1
  14. package/dist/chains/index.js +2 -2
  15. package/dist/chains-BJrZs6sG.js +57 -0
  16. package/dist/chains-BJrZs6sG.js.map +1 -0
  17. package/dist/chains-BYRTTipa.cjs +73 -0
  18. package/dist/changeOwnership-B7bEgla4.d.cts +509 -0
  19. package/dist/changeOwnership-B7bEgla4.d.cts.map +1 -0
  20. package/dist/changeOwnership-DwgtdSFc.d.ts +509 -0
  21. package/dist/changeOwnership-DwgtdSFc.d.ts.map +1 -0
  22. package/dist/entity/index.cjs +19 -0
  23. package/dist/entity/index.d.cts +4 -0
  24. package/dist/entity/index.d.ts +4 -0
  25. package/dist/entity/index.js +6 -0
  26. package/dist/entity-B0t6sN7F.cjs +71 -0
  27. package/dist/entity-BIp_df9u.js +67 -0
  28. package/dist/entity-BIp_df9u.js.map +1 -0
  29. package/dist/entity-CFhbAv0R.js +70 -0
  30. package/dist/entity-CFhbAv0R.js.map +1 -0
  31. package/dist/entity-wfBnZd91.cjs +98 -0
  32. package/dist/errors-D-eJkGVc.cjs +56 -0
  33. package/dist/errors-DGB8AgT2.js +33 -0
  34. package/dist/errors-DGB8AgT2.js.map +1 -0
  35. package/dist/expiry-BUYIXTGb.cjs +135 -0
  36. package/dist/expiry-BxOlvVWC.d.ts +79 -0
  37. package/dist/expiry-BxOlvVWC.d.ts.map +1 -0
  38. package/dist/expiry-DGRzrSan.js +95 -0
  39. package/dist/expiry-DGRzrSan.js.map +1 -0
  40. package/dist/expiry-DRV29F1r.d.cts +79 -0
  41. package/dist/expiry-DRV29F1r.d.cts.map +1 -0
  42. package/dist/flags-0Vl5rrHq.d.cts +41 -0
  43. package/dist/flags-0Vl5rrHq.d.cts.map +1 -0
  44. package/dist/flags-Ct8k0gpB.cjs +43 -0
  45. package/dist/flags-ZtlvOIIk.js +33 -0
  46. package/dist/flags-ZtlvOIIk.js.map +1 -0
  47. package/dist/flags-wmcMoAnN.d.ts +41 -0
  48. package/dist/flags-wmcMoAnN.d.ts.map +1 -0
  49. package/dist/index-Cgw1u5aY.d.cts +278 -0
  50. package/dist/index-Cgw1u5aY.d.cts.map +1 -0
  51. package/dist/index-ClgIuxJ8.d.ts +159 -0
  52. package/dist/index-ClgIuxJ8.d.ts.map +1 -0
  53. package/dist/index-D0CmtiWT.d.cts +159 -0
  54. package/dist/index-D0CmtiWT.d.cts.map +1 -0
  55. package/dist/index-D8LZt-3J.d.cts +417 -0
  56. package/dist/index-D8LZt-3J.d.cts.map +1 -0
  57. package/dist/index-DVYFaXNV.d.ts +417 -0
  58. package/dist/index-DVYFaXNV.d.ts.map +1 -0
  59. package/dist/index-VCqgn29D.d.ts +278 -0
  60. package/dist/index-VCqgn29D.d.ts.map +1 -0
  61. package/dist/index.cjs +675 -357
  62. package/dist/index.d.cts +251 -203
  63. package/dist/index.d.cts.map +1 -1
  64. package/dist/index.d.ts +251 -203
  65. package/dist/index.d.ts.map +1 -1
  66. package/dist/index.js +626 -355
  67. package/dist/index.js.map +1 -1
  68. package/dist/payload-Bnw1hrvb.cjs +83 -0
  69. package/dist/payload-CIxSn_DW.js +67 -0
  70. package/dist/payload-CIxSn_DW.js.map +1 -0
  71. package/dist/payload-Dgx3PQeK.d.ts +94 -0
  72. package/dist/payload-Dgx3PQeK.d.ts.map +1 -0
  73. package/dist/payload-P0nWyjLg.d.cts +94 -0
  74. package/dist/payload-P0nWyjLg.d.cts.map +1 -0
  75. package/dist/query/index.cjs +24 -149
  76. package/dist/query/index.d.cts +5 -3
  77. package/dist/query/index.d.ts +5 -3
  78. package/dist/query/index.js +6 -137
  79. package/dist/queryBuilder-BXDvX1K_.cjs +1123 -0
  80. package/dist/queryBuilder-CbLL4pG5.js +963 -0
  81. package/dist/queryBuilder-CbLL4pG5.js.map +1 -0
  82. package/dist/selection-BvK6yX0g.d.cts +433 -0
  83. package/dist/selection-BvK6yX0g.d.cts.map +1 -0
  84. package/dist/selection-fpmI3ADQ.d.ts +433 -0
  85. package/dist/selection-fpmI3ADQ.d.ts.map +1 -0
  86. package/dist/types/index.cjs +4 -1
  87. package/dist/types/index.d.cts +7 -2
  88. package/dist/types/index.d.ts +7 -2
  89. package/dist/types/index.js +4 -1
  90. package/dist/utils/index.cjs +4 -17
  91. package/dist/utils/index.d.cts +3 -17
  92. package/dist/utils/index.d.ts +3 -17
  93. package/dist/utils/index.js +4 -18
  94. package/package.json +26 -5
  95. package/src/actions/public/getEntity.ts +27 -43
  96. package/src/actions/public/getEntityNonce.ts +30 -0
  97. package/src/actions/public/predictEntityKeys.test.ts +143 -0
  98. package/src/actions/public/predictEntityKeys.ts +138 -0
  99. package/src/actions/public/query.ts +33 -69
  100. package/src/actions/public/watchEntityEvents.test.ts +414 -0
  101. package/src/actions/public/watchEntityEvents.ts +225 -0
  102. package/src/actions/wallet/changeOwnership.ts +8 -8
  103. package/src/actions/wallet/createEntity.ts +104 -26
  104. package/src/actions/wallet/deleteEntity.ts +2 -3
  105. package/src/actions/wallet/extendEntity.ts +39 -16
  106. package/src/actions/wallet/mutateEntities.test.ts +76 -0
  107. package/src/actions/wallet/mutateEntities.ts +29 -74
  108. package/src/actions/wallet/patchEntity.ts +90 -0
  109. package/src/attr/attributes.test.ts +120 -0
  110. package/src/attr/attributes.ts +190 -0
  111. package/src/attr/codec.test.ts +162 -0
  112. package/src/attr/codec.ts +220 -0
  113. package/src/attr/decimal.ts +86 -0
  114. package/src/attr/errors.ts +145 -0
  115. package/src/attr/index.ts +50 -0
  116. package/src/attr/names.ts +106 -0
  117. package/src/attr/types.ts +150 -0
  118. package/src/attr/values.test.ts +271 -0
  119. package/src/attr/values.ts +502 -0
  120. package/src/chains/cheesecake.ts +22 -0
  121. package/src/chains/index.ts +1 -2
  122. package/src/clients/createPublicClient.test.ts +3 -5
  123. package/src/clients/createPublicClient.ts +5 -4
  124. package/src/clients/createWalletClient.test.ts +2 -2
  125. package/src/clients/createWalletClient.ts +13 -7
  126. package/src/clients/decorators/arkivPublic.ts +160 -139
  127. package/src/clients/decorators/arkivWallet.ts +91 -95
  128. package/src/consts.ts +2 -1
  129. package/src/entity/create.test.ts +184 -0
  130. package/src/entity/errors.ts +38 -0
  131. package/src/entity/events.ts +16 -0
  132. package/src/entity/expiry.test.ts +195 -0
  133. package/src/entity/expiry.ts +181 -0
  134. package/src/entity/flags.ts +65 -0
  135. package/src/entity/index.ts +14 -0
  136. package/src/entity/key.ts +68 -0
  137. package/src/entity/operations.ts +134 -0
  138. package/src/entity/params.ts +15 -0
  139. package/src/errors.ts +33 -28
  140. package/src/index.ts +6 -0
  141. package/src/query/engine.ts +59 -134
  142. package/src/query/errors.ts +151 -0
  143. package/src/query/expression.test.ts +286 -0
  144. package/src/query/expression.ts +493 -0
  145. package/src/query/index.ts +40 -4
  146. package/src/query/injection.test.ts +151 -0
  147. package/src/query/queryBuilder.test.ts +322 -637
  148. package/src/query/queryBuilder.ts +126 -359
  149. package/src/query/queryResult.ts +54 -51
  150. package/src/query/selection.test.ts +48 -98
  151. package/src/query/selection.ts +77 -65
  152. package/src/types/entity.test.ts +44 -155
  153. package/src/types/entity.ts +62 -44
  154. package/src/types/events.ts +64 -23
  155. package/src/types/index.ts +20 -16
  156. package/src/types/rpcSchema.ts +70 -33
  157. package/src/utils/arkivTransactions.test.ts +681 -38
  158. package/src/utils/arkivTransactions.ts +249 -104
  159. package/src/utils/chains.test.ts +21 -24
  160. package/src/utils/compression.ts +1 -1
  161. package/src/utils/entities.test.ts +62 -0
  162. package/src/utils/entities.ts +111 -29
  163. package/src/utils/expirationTime.ts +114 -10
  164. package/src/utils/logger.ts +2 -2
  165. package/src/utils/payload.ts +4 -2
  166. package/src/utils/revert.test.ts +99 -0
  167. package/src/utils/revert.ts +145 -0
  168. package/dist/chains-BifaY04V.cjs +0 -108
  169. package/dist/chains-CrtkVbBh.js +0 -86
  170. package/dist/chains-CrtkVbBh.js.map +0 -1
  171. package/dist/consts-AUoL-v42.cjs +0 -18
  172. package/dist/consts-BV4PR-de.js +0 -7
  173. package/dist/consts-BV4PR-de.js.map +0 -1
  174. package/dist/entity-B6g_EPm1.d.cts +0 -382
  175. package/dist/entity-B6g_EPm1.d.cts.map +0 -1
  176. package/dist/entity-qoLMAuuk.d.ts +0 -382
  177. package/dist/entity-qoLMAuuk.d.ts.map +0 -1
  178. package/dist/index-DdspxNAA.d.ts +0 -481
  179. package/dist/index-DdspxNAA.d.ts.map +0 -1
  180. package/dist/index-Ds9dwqb5.d.cts +0 -481
  181. package/dist/index-Ds9dwqb5.d.cts.map +0 -1
  182. package/dist/payload-B2kFJyah.d.ts +0 -6
  183. package/dist/payload-B2kFJyah.d.ts.map +0 -1
  184. package/dist/payload-CJN8fTvX.d.cts +0 -6
  185. package/dist/payload-CJN8fTvX.d.cts.map +0 -1
  186. package/dist/payload-Claf2jUO.js +0 -13
  187. package/dist/payload-Claf2jUO.js.map +0 -1
  188. package/dist/payload-DpA5PA-w.cjs +0 -23
  189. package/dist/query/index.js.map +0 -1
  190. package/dist/queryBuilder-D2uwP9jI.js +0 -704
  191. package/dist/queryBuilder-D2uwP9jI.js.map +0 -1
  192. package/dist/queryBuilder-UW2MCaX4.cjs +0 -792
  193. package/dist/utils/index.d.cts.map +0 -1
  194. package/dist/utils/index.d.ts.map +0 -1
  195. package/dist/utils/index.js.map +0 -1
  196. package/src/actions/public/buildQuery.ts +0 -10
  197. package/src/actions/public/subscribeEntityEvents.ts +0 -119
  198. package/src/actions/wallet/updateEntity.ts +0 -60
  199. package/src/chains/braga.ts +0 -26
  200. package/src/chains/kaolin.ts +0 -29
  201. package/src/query/engine.test.ts +0 -526
  202. package/src/query/predicate.test.ts +0 -461
  203. package/src/query/predicate.ts +0 -144
  204. package/src/query/queryResult.test.ts +0 -270
  205. package/src/query/selectQueryBuilder.test.ts +0 -210
  206. package/src/types/attributes.ts +0 -16
  207. package/src/utils/validation.test.ts +0 -60
  208. package/src/utils/validation.ts +0 -32
  209. /package/dist/{types-H-TSmRpm.js → types-Bx1EKe0v.js} +0 -0
  210. /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
+ }