@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.
Files changed (184) hide show
  1. package/README.md +180 -61
  2. package/dist/chains/index.d.ts +17 -17
  3. package/dist/changeOwnership-Cy9gfyjN.d.cts +1160 -0
  4. package/dist/changeOwnership-Cy9gfyjN.d.cts.map +1 -0
  5. package/dist/changeOwnership-gXf-lBqI.d.ts +1160 -0
  6. package/dist/changeOwnership-gXf-lBqI.d.ts.map +1 -0
  7. package/dist/entity/index.cjs +4 -10
  8. package/dist/entity/index.d.cts +3 -3
  9. package/dist/entity/index.d.ts +3 -3
  10. package/dist/entity/index.js +3 -4
  11. package/dist/entity-BM1unrQf.cjs +41 -0
  12. package/dist/{entity-DJ0Gml9c.cjs → entity-BgaFJSaV.cjs} +4 -4
  13. package/dist/entity-CFAI1KyG.js +36 -0
  14. package/dist/entity-CFAI1KyG.js.map +1 -0
  15. package/dist/{entity-CniBgBSd.js → entity-CGqx_AU0.js} +4 -4
  16. package/dist/entity-CGqx_AU0.js.map +1 -0
  17. package/dist/{payload-AfUfSctb.d.ts → expirationTime-C7PZK49K.d.ts} +8 -37
  18. package/dist/expirationTime-C7PZK49K.d.ts.map +1 -0
  19. package/dist/expirationTime-CkIGNAg8.cjs +140 -0
  20. package/dist/{payload-D167BlJR.d.cts → expirationTime-DOB8V4B8.d.cts} +8 -37
  21. package/dist/expirationTime-DOB8V4B8.d.cts.map +1 -0
  22. package/dist/{expiry-Cgdb_dbq.js → expirationTime-UKPuA-LM.js} +62 -27
  23. package/dist/expirationTime-UKPuA-LM.js.map +1 -0
  24. package/dist/expiry-B48I-yTA.d.cts +40 -0
  25. package/dist/expiry-B48I-yTA.d.cts.map +1 -0
  26. package/dist/expiry-S2PUL8Hg.d.ts +40 -0
  27. package/dist/expiry-S2PUL8Hg.d.ts.map +1 -0
  28. package/dist/index-C-mMF5WZ.d.cts +735 -0
  29. package/dist/index-C-mMF5WZ.d.cts.map +1 -0
  30. package/dist/{index-BkqSnGJH.d.ts → index-C4zZqJ9S.d.ts} +5 -27
  31. package/dist/index-C4zZqJ9S.d.ts.map +1 -0
  32. package/dist/{index-CLhsFyVH.d.cts → index-CcVWYB6v.d.cts} +5 -27
  33. package/dist/index-CcVWYB6v.d.cts.map +1 -0
  34. package/dist/index-DCx2XC6S.d.ts +489 -0
  35. package/dist/index-DCx2XC6S.d.ts.map +1 -0
  36. package/dist/index-DLb7BfIw.d.ts +735 -0
  37. package/dist/index-DLb7BfIw.d.ts.map +1 -0
  38. package/dist/index-DzriOUm8.d.cts +489 -0
  39. package/dist/index-DzriOUm8.d.cts.map +1 -0
  40. package/dist/index.cjs +162 -146
  41. package/dist/index.d.cts +180 -133
  42. package/dist/index.d.cts.map +1 -1
  43. package/dist/index.d.ts +180 -133
  44. package/dist/index.d.ts.map +1 -1
  45. package/dist/index.js +93 -83
  46. package/dist/index.js.map +1 -1
  47. package/dist/params-BuIrJA9a.cjs +45 -0
  48. package/dist/params-DDJOnsNi.js +22 -0
  49. package/dist/params-DDJOnsNi.js.map +1 -0
  50. package/dist/query/index.cjs +9 -4
  51. package/dist/query/index.d.cts +6 -6
  52. package/dist/query/index.d.ts +6 -6
  53. package/dist/query/index.js +4 -5
  54. package/dist/query-D0KtuFh2.cjs +1625 -0
  55. package/dist/query-DpRDozmj.js +1417 -0
  56. package/dist/query-DpRDozmj.js.map +1 -0
  57. package/dist/schema/index.cjs +46 -0
  58. package/dist/schema/index.d.cts +2 -0
  59. package/dist/schema/index.d.ts +2 -0
  60. package/dist/schema/index.js +3 -0
  61. package/dist/{attr-BXTkpnoc.cjs → schema-B9AyXcgU.cjs} +513 -246
  62. package/dist/{attr-tmyREHtw.js → schema-DY4sqehl.js} +427 -244
  63. package/dist/schema-DY4sqehl.js.map +1 -0
  64. package/dist/types/index.cjs +1 -1
  65. package/dist/types/index.d.cts +5 -5
  66. package/dist/types/index.d.ts +5 -5
  67. package/dist/types/index.js +1 -1
  68. package/dist/utils/index.cjs +3 -5
  69. package/dist/utils/index.d.cts +3 -3
  70. package/dist/utils/index.d.ts +3 -3
  71. package/dist/utils/index.js +3 -3
  72. package/package.json +9 -12
  73. package/src/actions/public/getEntity.test.ts +69 -0
  74. package/src/actions/public/getEntity.ts +34 -20
  75. package/src/actions/public/predictEntityKeys.ts +8 -7
  76. package/src/actions/public/query.test.ts +58 -0
  77. package/src/actions/public/query.ts +41 -13
  78. package/src/actions/wallet/createEntity.ts +34 -63
  79. package/src/actions/wallet/executeBatch.test.ts +38 -0
  80. package/src/actions/wallet/executeBatch.ts +26 -9
  81. package/src/actions/wallet/extendEntity.ts +9 -8
  82. package/src/actions/wallet/patchEntity.ts +43 -52
  83. package/src/clients/createPublicClient.test.ts +10 -2
  84. package/src/clients/createWalletClient.ts +4 -1
  85. package/src/clients/decorators/arkivPublic.ts +123 -80
  86. package/src/clients/decorators/arkivWallet.ts +113 -65
  87. package/src/entity/create.test.ts +25 -21
  88. package/src/entity/expiry.ts +8 -6
  89. package/src/entity/flags.ts +1 -1
  90. package/src/entity/index.ts +2 -4
  91. package/src/entity/key.ts +3 -3
  92. package/src/entity/operations.ts +1 -1
  93. package/src/errors.ts +2 -11
  94. package/src/index.ts +18 -6
  95. package/src/query/builderBase.ts +166 -0
  96. package/src/query/countBuilder.test.ts +91 -0
  97. package/src/query/countBuilder.ts +67 -0
  98. package/src/query/engine.ts +9 -3
  99. package/src/query/errors.ts +24 -14
  100. package/src/query/expression.test.ts +129 -21
  101. package/src/query/expression.ts +323 -94
  102. package/src/query/index.ts +26 -4
  103. package/src/query/injection.test.ts +5 -11
  104. package/src/query/queryBuilder.test.ts +467 -58
  105. package/src/query/queryBuilder.ts +191 -98
  106. package/src/query/queryResult.ts +35 -9
  107. package/src/query/selection.test.ts +278 -24
  108. package/src/query/selection.ts +427 -86
  109. package/src/schema/attributes.test.ts +266 -0
  110. package/src/schema/attributes.ts +212 -0
  111. package/src/{attr → schema}/codec.test.ts +37 -5
  112. package/src/{attr → schema}/codec.ts +40 -11
  113. package/src/schema/column.ts +193 -0
  114. package/src/{attr → schema}/errors.ts +67 -10
  115. package/src/schema/index.ts +76 -0
  116. package/src/{attr → schema}/names.ts +4 -2
  117. package/src/schema/schema.test.ts +171 -0
  118. package/src/schema/schema.ts +250 -0
  119. package/src/{attr → schema}/types.ts +36 -30
  120. package/src/{attr → schema}/values.test.ts +62 -3
  121. package/src/{attr → schema}/values.ts +246 -65
  122. package/src/types/entity.test.ts +1 -44
  123. package/src/types/entity.ts +9 -55
  124. package/src/types/events.ts +1 -1
  125. package/src/types/index.ts +11 -4
  126. package/src/types/rpcSchema.ts +22 -16
  127. package/src/utils/arkivTransactions.test.ts +356 -116
  128. package/src/utils/arkivTransactions.ts +30 -38
  129. package/src/utils/entities.ts +10 -9
  130. package/src/utils/expirationTime.ts +5 -5
  131. package/src/utils/index.ts +0 -1
  132. package/src/utils/logger.test.ts +45 -0
  133. package/src/utils/logger.ts +5 -39
  134. package/src/utils/revert.test.ts +25 -0
  135. package/src/utils/revert.ts +28 -3
  136. package/dist/attr/index.cjs +0 -34
  137. package/dist/attr/index.d.cts +0 -2
  138. package/dist/attr/index.d.ts +0 -2
  139. package/dist/attr/index.js +0 -3
  140. package/dist/attr-tmyREHtw.js.map +0 -1
  141. package/dist/entity-B0t6sN7F.cjs +0 -71
  142. package/dist/entity-BIp_df9u.js +0 -67
  143. package/dist/entity-BIp_df9u.js.map +0 -1
  144. package/dist/entity-CniBgBSd.js.map +0 -1
  145. package/dist/expiry-BxOlvVWC.d.ts +0 -79
  146. package/dist/expiry-BxOlvVWC.d.ts.map +0 -1
  147. package/dist/expiry-CG9GhVXv.cjs +0 -135
  148. package/dist/expiry-Cgdb_dbq.js.map +0 -1
  149. package/dist/expiry-DRV29F1r.d.cts +0 -79
  150. package/dist/expiry-DRV29F1r.d.cts.map +0 -1
  151. package/dist/flags-DCD26uhe.cjs +0 -43
  152. package/dist/flags-DUT2gHEU.js +0 -33
  153. package/dist/flags-DUT2gHEU.js.map +0 -1
  154. package/dist/index-BkqSnGJH.d.ts.map +0 -1
  155. package/dist/index-C7fbakoI.d.cts +0 -278
  156. package/dist/index-C7fbakoI.d.cts.map +0 -1
  157. package/dist/index-CIcHPx2j.d.cts +0 -424
  158. package/dist/index-CIcHPx2j.d.cts.map +0 -1
  159. package/dist/index-CLhsFyVH.d.cts.map +0 -1
  160. package/dist/index-Cd2aaWme.d.ts +0 -424
  161. package/dist/index-Cd2aaWme.d.ts.map +0 -1
  162. package/dist/index-Cq6fa8AI.d.ts +0 -278
  163. package/dist/index-Cq6fa8AI.d.ts.map +0 -1
  164. package/dist/index-DaNeJUw8.d.ts +0 -955
  165. package/dist/index-DaNeJUw8.d.ts.map +0 -1
  166. package/dist/index-Dv16LO2s.d.cts +0 -955
  167. package/dist/index-Dv16LO2s.d.cts.map +0 -1
  168. package/dist/payload-AfUfSctb.d.ts.map +0 -1
  169. package/dist/payload-D167BlJR.d.cts.map +0 -1
  170. package/dist/payload-DE7wMztL.js +0 -92
  171. package/dist/payload-DE7wMztL.js.map +0 -1
  172. package/dist/payload-DqFweqiO.cjs +0 -108
  173. package/dist/query-9oojUdM7.cjs +0 -1128
  174. package/dist/query-Ber-lOJQ.js +0 -968
  175. package/dist/query-Ber-lOJQ.js.map +0 -1
  176. package/src/actions/public/getEntityCount.ts +0 -10
  177. package/src/attr/attributes.test.ts +0 -120
  178. package/src/attr/attributes.ts +0 -196
  179. package/src/attr/index.ts +0 -50
  180. package/src/types/mimeTypes.ts +0 -27
  181. package/src/utils/compression.test.ts +0 -32
  182. package/src/utils/compression.ts +0 -102
  183. package/src/utils/payload.ts +0 -36
  184. /package/src/{attr → schema}/decimal.ts +0 -0
@@ -13,8 +13,6 @@ import type { CreateEntityParameters } from "../actions/wallet/createEntity"
13
13
  import type { DeleteEntityParameters } from "../actions/wallet/deleteEntity"
14
14
  import type { ExtendEntityParameters } from "../actions/wallet/extendEntity"
15
15
  import type { PatchEntityParameters } from "../actions/wallet/patchEntity"
16
- // Internal seam: the ABI encoders are not part of the package's public surface.
17
- import { encodeAttributes, encodeMutations } from "../attr/attributes"
18
16
  import type { ArkivClient } from "../clients/baseClient"
19
17
  import type { WalletArkivClient } from "../clients/createWalletClient"
20
18
  import { ARKIV_ADDRESS } from "../consts"
@@ -31,7 +29,9 @@ import {
31
29
  patchOperation,
32
30
  transferOwnershipOperation,
33
31
  } from "../entity/operations"
34
- import { EmptyPatchError, EntityMutationError, InvalidContentTypeError } from "../errors"
32
+ import { EmptyPatchError, EntityMutationError } from "../errors"
33
+ // Internal seam: the ABI encoders are not part of the package's public surface.
34
+ import { encodeCreate, encodePatch } from "../schema/attributes"
35
35
  import type { TxParams } from "../types"
36
36
 
37
37
  import { getLogger } from "./logger"
@@ -39,16 +39,7 @@ import { describeEntityRevert } from "./revert"
39
39
 
40
40
  const logger = getLogger("utils:arkiv-transactions")
41
41
 
42
- // RFC 2045 token chars, lowercase only. Rejects uppercase to prevent text/plain vs Text/Plain ambiguity.
43
- const MIME_REGEX = /^[a-z][a-z0-9!#$&\-^_]*\/[a-z0-9][a-z0-9!#$&\-^_.+]*$/
44
-
45
- function validateContentType(contentType: string): void {
46
- if (!MIME_REGEX.test(contentType)) {
47
- throw new InvalidContentTypeError(contentType)
48
- }
49
- }
50
-
51
- export const ENTITY_ERRORS_ABI = parseAbi([
42
+ const ENTITY_ERRORS_ABI = parseAbi([
52
43
  // Batch and operation framing.
53
44
  "error EmptyBatch()",
54
45
  "error InvalidOpType(uint8 operation)",
@@ -70,8 +61,8 @@ export const ENTITY_ERRORS_ABI = parseAbi([
70
61
  "error NotOwner(bytes32 entityKey, address caller, address owner)",
71
62
  "error ReadOnlyEntity(bytes32 entityKey)",
72
63
  "error ReservedCreationFlags(uint8 creationFlags)",
73
- // Expiry. The engine bounds an expiry only by the width of the field and by liveness — there is
74
- // no MAX_EXPIRES_AT or MAX_LIFETIME, so there is no error for exceeding one.
64
+ // Expiry. The engine bounds an expiry only by the uint64 width of the field — the SDK's
65
+ // MAX_EXPIRES_AT — and by liveness. There is no separate cap on lifetime, so no error for one.
75
66
  "error ExpiryDeadOnArrival(uint64 target, uint64 currentBlock)",
76
67
  "error ExpiryNotExtended(bytes32 entityKey, uint64 newExpiresAt, uint64 currentExpiresAt)",
77
68
  // Ownership transfer.
@@ -94,11 +85,11 @@ export type SendArkivTransactionResult = {
94
85
  export async function sendArkivTransaction(
95
86
  client: ArkivClient,
96
87
  ops: {
97
- creates?: CreateEntityParameters[]
98
- patches?: PatchEntityParameters[]
99
- deletes?: DeleteEntityParameters[]
100
- extensions?: ExtendEntityParameters[]
101
- ownershipChanges?: ChangeOwnershipParameters[]
88
+ creates?: readonly CreateEntityParameters[] | undefined
89
+ patches?: readonly PatchEntityParameters[] | undefined
90
+ deletes?: readonly DeleteEntityParameters[] | undefined
91
+ extensions?: readonly ExtendEntityParameters[] | undefined
92
+ ownershipChanges?: readonly ChangeOwnershipParameters[] | undefined
102
93
  },
103
94
  txParams?: TxParams,
104
95
  ): Promise<SendArkivTransactionResult> {
@@ -108,6 +99,21 @@ export async function sendArkivTransaction(
108
99
 
109
100
  const { creates, patches, deletes, extensions, ownershipChanges } = ops
110
101
 
102
+ // Every write is checked against its schema before the block read, so one that does not fit
103
+ // fails without touching the network — and a network failure cannot mask it.
104
+ const encodedCreates = (creates ?? []).map((item) => ({
105
+ item,
106
+ attributes: encodeCreate(item.schema, item.values),
107
+ }))
108
+
109
+ const patchOps = (patches ?? []).map((item) => {
110
+ const mutations = encodePatch(item.schema, item)
111
+ if (mutations.length === 0) {
112
+ throw new EmptyPatchError(item.entityKey)
113
+ }
114
+ return patchOperation({ entityKey: item.entityKey, mutations })
115
+ })
116
+
111
117
  // The block height, to resolve any relative expiry — which both creates and extensions carry.
112
118
  // Skipped entirely for a batch with neither.
113
119
  const currentBlock =
@@ -115,37 +121,23 @@ export async function sendArkivTransaction(
115
121
 
116
122
  const expiryContext = { currentBlock }
117
123
 
118
- const createOps = (creates ?? []).map((item) => {
119
- validateContentType(item.contentType)
120
-
124
+ const createOps = encodedCreates.map(({ item, attributes }) => {
121
125
  const salt = item.salt === undefined ? randomSalt() : resolveSalt(item.salt)
122
- const expiry = resolveExpiry(item.expires, expiryContext)
126
+ const expiry = resolveExpiry(item.expiration, expiryContext)
123
127
 
124
128
  return createOperation({
125
129
  salt,
126
130
  expiry,
127
131
  creationFlags: encodeCreationFlags(item.flags),
128
- attributes: encodeAttributes(item.attributes, {
129
- payload: item.payload,
130
- contentType: item.contentType,
131
- }),
132
+ attributes,
132
133
  })
133
134
  })
134
135
 
135
- const patchOps = (patches ?? []).map((item) => {
136
- if (item.contentType !== undefined) validateContentType(item.contentType)
137
- const mutations = encodeMutations(item)
138
- if (mutations.length === 0) {
139
- throw new EmptyPatchError(item.entityKey)
140
- }
141
- return patchOperation({ entityKey: item.entityKey, mutations })
142
- })
143
-
144
136
  // An extension resolves its expiry exactly as a create does — the engine applies the same
145
137
  // `max(expiresAt, currentBlock + minLifetime)` to both — so the same bounds are checked here
146
138
  // rather than left to a revert.
147
139
  const extendOps = (extensions ?? []).map((item) => {
148
- const expiry = resolveExpiry(item.expires, expiryContext)
140
+ const expiry = resolveExpiry(item.expiration, expiryContext)
149
141
  return extendExpiryOperation({ entityKey: item.entityKey, expiry })
150
142
  })
151
143
 
@@ -1,12 +1,12 @@
1
- import { checksumAddress, type Hex, hexToBytes } from "viem"
2
- import type { AttributeSchema, Attributes } from "../attr"
3
- // Internal seam: the JSON-RPC decoder is not part of the package's public surface.
4
- import { asTypeTag, decodeRpcValue } from "../attr/codec"
1
+ import { checksumAddress, type Hex } from "viem"
5
2
  import {
6
3
  decodeCreationFlags,
7
4
  encodeCreationFlags,
8
5
  type ResolvedCreationFlags,
9
6
  } from "../entity/flags"
7
+ import type { AttributeSchema, Attributes } from "../schema"
8
+ // Internal seam: the JSON-RPC decoder is not part of the package's public surface.
9
+ import { asTypeTag, decodeRpcValue } from "../schema/codec"
10
10
  import { Entity, type EntityFields } from "../types/entity"
11
11
  import type { RpcEntity } from "../types/rpcSchema"
12
12
  import { getLogger } from "./logger"
@@ -29,9 +29,12 @@ function decodeAttributes(rpcAttributes: NonNullable<RpcEntity["attributes"]>):
29
29
 
30
30
  /** The same, for the values-free `attributeSchema` projection. */
31
31
  function decodeSchema(entries: NonNullable<RpcEntity["attributeSchema"]>): AttributeSchema {
32
- const schema: Record<string, ReturnType<typeof asTypeTag>> = {}
33
- for (const { name, type } of entries) {
34
- schema[name] = asTypeTag(type)
32
+ const schema: Record<string, AttributeSchema[string]> = {}
33
+ for (const { name, type, indexed } of entries) {
34
+ schema[name] = {
35
+ type: asTypeTag(type),
36
+ indexed: typeof indexed === "boolean" ? indexed : undefined,
37
+ }
35
38
  }
36
39
  return schema
37
40
  }
@@ -53,8 +56,6 @@ export function entityFromRpcResult(rpcEntity: RpcEntity): Entity {
53
56
  updatedAt: toBlock(rpcEntity.updatedAt),
54
57
  expiresAt: toBlock(rpcEntity.expiresAt),
55
58
  creationFlags: decodeFlags(rpcEntity.creationFlags),
56
- contentType: rpcEntity.contentType,
57
- payload: rpcEntity.payload !== undefined ? hexToBytes(rpcEntity.payload) : undefined,
58
59
  attributeSchema:
59
60
  rpcEntity.attributeSchema !== undefined ? decodeSchema(rpcEntity.attributeSchema) : undefined,
60
61
  attributes:
@@ -23,7 +23,7 @@ function deadlineOf(expiresAt: bigint | Date, options?: DeadlineOptions): Expiry
23
23
  }
24
24
 
25
25
  /**
26
- * Builds the `expires` of a create or an extension.
26
+ * Builds the `expiration` of a create or an extension.
27
27
  *
28
28
  * Every helper returns the `{ minLifetime, expiresAt }` pair the ABI carries, so what you write is
29
29
  * what goes on the wire. The `from*` helpers fill `minLifetime` and leave `expiresAt` at `0n`;
@@ -34,14 +34,14 @@ function deadlineOf(expiresAt: bigint | Date, options?: DeadlineOptions): Expiry
34
34
  * object.
35
35
  *
36
36
  * @example The 90% case: a duration, and nothing else to think about.
37
- * await client.createEntity({ payload, contentType, expires: ExpirationTime.fromDays(30) })
37
+ * await client.createEntity(Posts, values, { expiration: ExpirationTime.fromDays(30) })
38
38
  *
39
39
  * @example An exact block, or a wall-clock date.
40
- * expires: ExpirationTime.atBlock(1_200_000n)
41
- * expires: ExpirationTime.atDate(new Date("2027-01-01"))
40
+ * expiration: ExpirationTime.atBlock(1_200_000n)
41
+ * expiration: ExpirationTime.atDate(new Date("2027-01-01"))
42
42
  *
43
43
  * @example A deadline that still guarantees a minimum life.
44
- * expires: ExpirationTime.atBlock(1_200_000n, { atLeast: ExpirationTime.fromDays(1) })
44
+ * expiration: ExpirationTime.atBlock(1_200_000n, { atLeast: ExpirationTime.fromDays(1) })
45
45
  */
46
46
  export const ExpirationTime = {
47
47
  /**
@@ -1,3 +1,2 @@
1
1
  export type { Expiry, Lifetime } from "../entity/expiry"
2
2
  export * from "./expirationTime"
3
- export * from "./payload"
@@ -0,0 +1,45 @@
1
+ import { afterEach, beforeEach, describe, expect, spyOn, test } from "bun:test"
2
+ import { getLogger } from "./logger"
3
+
4
+ describe("getLogger", () => {
5
+ const originalDebug = process.env.DEBUG
6
+ let log: ReturnType<typeof spyOn<Console, "log">>
7
+
8
+ beforeEach(() => {
9
+ log = spyOn(console, "log").mockImplementation(() => {})
10
+ })
11
+
12
+ afterEach(() => {
13
+ log.mockRestore()
14
+ if (originalDebug === undefined) delete process.env.DEBUG
15
+ else process.env.DEBUG = originalDebug
16
+ })
17
+
18
+ /** Which of `scopes` log anything under `debug`. */
19
+ function enabledScopes(debug: string, scopes: string[]): string[] {
20
+ process.env.DEBUG = debug
21
+ return scopes.filter((scope) => {
22
+ log.mockClear()
23
+ getLogger(scope)("hello")
24
+ return log.mock.calls.length > 0
25
+ })
26
+ }
27
+
28
+ test("logs only the namespaces a pattern names", () => {
29
+ expect(enabledScopes("arkiv:a", ["a", "b"])).toEqual(["a"])
30
+ })
31
+
32
+ test("a wildcard enables every namespace under its prefix", () => {
33
+ expect(enabledScopes("arkiv:*", ["a", "b"])).toEqual(["a", "b"])
34
+ expect(enabledScopes("arkiv:a:*", ["a:x", "a:y", "b:x"])).toEqual(["a:x", "a:y"])
35
+ })
36
+
37
+ test("a -pattern excludes what it matches and leaves the rest enabled", () => {
38
+ expect(enabledScopes("arkiv:*,-arkiv:a", ["a", "b", "c"])).toEqual(["b", "c"])
39
+ expect(enabledScopes("arkiv:* -arkiv:a:*", ["a:x", "a:y", "b:x"])).toEqual(["b:x"])
40
+ })
41
+
42
+ test("an exclusion alone enables nothing", () => {
43
+ expect(enabledScopes("-arkiv:a", ["a", "b"])).toEqual([])
44
+ })
45
+ })
@@ -23,7 +23,8 @@ function getDebugEnv(): string {
23
23
  return ""
24
24
  }
25
25
 
26
- // Parse debug patterns and check if a namespace matches
26
+ // Check if a namespace matches a single positive pattern. Exclusions ("-pattern") are handled by
27
+ // isEnabled, which strips the "-" and calls this with the rest.
27
28
  function matchesPattern(namespace: string, pattern: string): boolean {
28
29
  // Exact match
29
30
  if (pattern === namespace) return true
@@ -34,12 +35,6 @@ function matchesPattern(namespace: string, pattern: string): boolean {
34
35
  return namespace.startsWith(prefix)
35
36
  }
36
37
 
37
- // Negative match: "-arkiv:query" excludes "arkiv:query"
38
- if (pattern.startsWith("-")) {
39
- const excludePattern = pattern.slice(1)
40
- return !matchesPattern(namespace, excludePattern)
41
- }
42
-
43
38
  return false
44
39
  }
45
40
 
@@ -58,8 +53,8 @@ function isEnabled(namespace: string): boolean {
58
53
  let enabled = false
59
54
  for (const pattern of patterns) {
60
55
  if (pattern.startsWith("-")) {
61
- // Exclude pattern - if it matches, disable
62
- if (matchesPattern(namespace, pattern)) {
56
+ // Exclude pattern: "-arkiv:query" disables "arkiv:query" whatever else enables it
57
+ if (matchesPattern(namespace, pattern.slice(1))) {
63
58
  return false
64
59
  }
65
60
  } else if (matchesPattern(namespace, pattern)) {
@@ -70,10 +65,6 @@ function isEnabled(namespace: string): boolean {
70
65
  return enabled
71
66
  }
72
67
 
73
- // Store enabled namespaces (for programmatic enable/disable)
74
- const enabledNamespaces: Set<string> = new Set()
75
- const disabledNamespaces: Set<string> = new Set()
76
-
77
68
  // Color palette for namespace coloring (distinct, readable colors)
78
69
  const COLOR_PALETTE = [
79
70
  "#0000FF", // blue
@@ -161,11 +152,7 @@ function createDebugLogger(namespace: string): DebugLogger {
161
152
  const colors = getColorForNamespace(namespace)
162
153
 
163
154
  return (...args: unknown[]) => {
164
- // Check if enabled via environment variable or programmatically
165
- const envEnabled = isEnabled(namespace)
166
- const progEnabled = enabledNamespaces.has(namespace) && !disabledNamespaces.has(namespace)
167
-
168
- if (envEnabled || progEnabled) {
155
+ if (isEnabled(namespace)) {
169
156
  if (isNode) {
170
157
  // Node.js: use ANSI color codes
171
158
  const coloredNamespace = `${ANSI_BOLD}${colors.ansi}${namespace}${ANSI_RESET}`
@@ -187,24 +174,3 @@ export const getLogger = (scope: string): DebugLogger => {
187
174
  const namespace = `${ROOT_NAMESPACE}:${scope}`
188
175
  return createDebugLogger(namespace)
189
176
  }
190
-
191
- export const enableDebug = (namespaces: string): void => {
192
- // Parse and add namespaces to enabled set
193
- const patterns = namespaces.split(/[\s,]+/).filter((p) => p.length > 0)
194
- for (const pattern of patterns) {
195
- if (pattern.startsWith("-")) {
196
- // Remove from enabled, add to disabled
197
- const namespace = pattern.slice(1)
198
- enabledNamespaces.delete(namespace)
199
- disabledNamespaces.add(namespace)
200
- } else {
201
- enabledNamespaces.add(pattern)
202
- disabledNamespaces.delete(pattern)
203
- }
204
- }
205
- }
206
-
207
- export const disableDebug = (): void => {
208
- enabledNamespaces.clear()
209
- disabledNamespaces.clear()
210
- }
@@ -65,6 +65,31 @@ describe("describeEntityRevert", () => {
65
65
  })
66
66
  })
67
67
 
68
+ describe("the indexed flag", () => {
69
+ it("says an indexed bytes attribute is the problem, not an unknown type", () => {
70
+ // bytes is typeId 7; bit 7 is the indexed flag.
71
+ expect(describeEntityRevert(reverted("InvalidValueType", [ident("body"), 0x87]))).toBe(
72
+ '"body" is a bytes attribute flagged as indexed, and bytes are stored but never indexed',
73
+ )
74
+ })
75
+
76
+ it("names the type and the flag when the node refuses an indexed attribute", () => {
77
+ // str is typeId 8; with bit 7 set, a node that predates the flag sees 0x88.
78
+ const message = describeEntityRevert(reverted("InvalidValueType", [ident("category"), 0x88]))
79
+ expect(message).toContain('"category" has type str and is flagged as indexed (type id 136)')
80
+ expect(message).toContain("may not support the indexed flag")
81
+ })
82
+ it("names the type when the node refuses an unindexed one", () => {
83
+ const message = describeEntityRevert(reverted("InvalidValueType", [ident("title"), 0x08]))
84
+ expect(message).toContain('"title" has type str (type id 8), which the node does not accept')
85
+ })
86
+ it("still reports a type id it cannot read as unknown", () => {
87
+ expect(describeEntityRevert(reverted("InvalidValueType", [ident("x"), 0xff]))).toContain(
88
+ "type id 255, which is not a known type",
89
+ )
90
+ })
91
+ })
92
+
68
93
  describe("entity state", () => {
69
94
  it("abbreviates the key so it does not bury the sentence", () => {
70
95
  const message = describeEntityRevert(reverted("EntityNotFound", [KEY]))
@@ -1,4 +1,6 @@
1
1
  import { BaseError, ContractFunctionRevertedError, type Hex, hexToString } from "viem"
2
+ // Internal seam: the typeId layout is the protocol's, not part of the package's public surface.
3
+ import { decodeTypeId } from "../schema/codec"
2
4
 
3
5
  /**
4
6
  * Turns a decoded engine revert into a sentence that says what to change.
@@ -44,8 +46,8 @@ function explain(name: string, args: readonly unknown[]): string | undefined {
44
46
  // ── the attribute list ───────────────────────────────────────────────────
45
47
  case "SystemAttributeNotWritable":
46
48
  return (
47
- `"${identName(args[0])}" is owned by the engine and cannot be written. Only $payload and ` +
48
- "$contentType are yours to set"
49
+ `"${identName(args[0])}" is owned by the engine and cannot be written. Names starting ` +
50
+ 'with "$" are reserved for system attributes'
49
51
  )
50
52
  case "TombstoneInCreate":
51
53
  return (
@@ -55,7 +57,7 @@ function explain(name: string, args: readonly unknown[]): string | undefined {
55
57
  case "TombstoneValueNotEmpty":
56
58
  return `the tombstone for "${identName(args[0])}" carries a value; unsetting takes no value`
57
59
  case "InvalidValueType":
58
- return `"${identName(args[0])}" names type id ${String(args[1])}, which is not a known type`
60
+ return describeInvalidType(identName(args[0]), Number(args[1]))
59
61
  case "AttributesNotSorted":
60
62
  return "attributes must be strictly ascending by name, which also makes each name unique"
61
63
  case "TooManyAttributes":
@@ -117,6 +119,29 @@ function explain(name: string, args: readonly unknown[]): string | undefined {
117
119
  }
118
120
  }
119
121
 
122
+ /**
123
+ * Why the engine refused a `typeId` — an indexed `bytes`, a known type it would not take (most
124
+ * likely the indexed flag, on a node that predates it), or a type it does not know.
125
+ */
126
+ function describeInvalidType(name: string, typeId: number): string {
127
+ try {
128
+ const { tag, indexed } = decodeTypeId(typeId)
129
+ if (tag === "bytes" && indexed) {
130
+ return `"${name}" is a bytes attribute flagged as indexed, and bytes are stored but never indexed`
131
+ }
132
+ if (indexed) {
133
+ return (
134
+ `"${name}" has type ${tag} and is flagged as indexed (type id ${typeId}), which the node ` +
135
+ "does not accept. The node may not support the indexed flag yet"
136
+ )
137
+ }
138
+ return `"${name}" has type ${tag} (type id ${typeId}), which the node does not accept`
139
+ } catch {
140
+ // Falls through to the generic reading: the low bits name no type the SDK knows either.
141
+ }
142
+ return `"${name}" names type id ${typeId}, which is not a known type`
143
+ }
144
+
120
145
  /** A `bytes32` Ident32 cell as the name it holds — left-aligned, null-padded. */
121
146
  function identName(raw: unknown): string {
122
147
  if (typeof raw !== "string") return String(raw)
@@ -1,34 +0,0 @@
1
- const require_attr = require('../attr-BXTkpnoc.cjs');
2
-
3
- exports.ConflictingMutationError = require_attr.ConflictingMutationError;
4
- exports.DECIMAL_SCALE = require_attr.DECIMAL_SCALE;
5
- exports.DECIMAL_UNIT = require_attr.DECIMAL_UNIT;
6
- exports.I32_MAX = require_attr.I32_MAX;
7
- exports.I32_MIN = require_attr.I32_MIN;
8
- exports.InvalidAttributeNameError = require_attr.InvalidAttributeNameError;
9
- exports.InvalidValueError = require_attr.InvalidValueError;
10
- exports.MAX_ATTRIBUTES = require_attr.MAX_ATTRIBUTES;
11
- exports.MAX_NAME_BYTES = require_attr.MAX_NAME_BYTES;
12
- exports.MAX_PAYLOAD_BYTES = require_attr.MAX_PAYLOAD_BYTES;
13
- exports.MAX_STRING_BYTES = require_attr.MAX_STRING_BYTES;
14
- exports.MissingValueError = require_attr.MissingValueError;
15
- exports.TooManyAttributesError = require_attr.TooManyAttributesError;
16
- exports.U256_MAX = require_attr.U256_MAX;
17
- exports.U64_MAX = require_attr.U64_MAX;
18
- exports.UnknownAttributeTypeError = require_attr.UnknownAttributeTypeError;
19
- exports.UntypedValueError = require_attr.UntypedValueError;
20
- exports.addr = require_attr.addr;
21
- exports.bool = require_attr.bool;
22
- exports.bytes32 = require_attr.bytes32;
23
- exports.dec = require_attr.dec;
24
- exports.decFromUnits = require_attr.decFromUnits;
25
- exports.decUnits = require_attr.decUnits;
26
- exports.i32 = require_attr.i32;
27
- exports.isArkivValue = require_attr.isArkivValue;
28
- exports.isValidAttributeName = require_attr.isValidAttributeName;
29
- exports.key = require_attr.key;
30
- exports.str = require_attr.str;
31
- exports.toValue = require_attr.toValue;
32
- exports.u256 = require_attr.u256;
33
- exports.u64 = require_attr.u64;
34
- exports.validateAttributeName = require_attr.validateAttributeName;
@@ -1,2 +0,0 @@
1
- import { A as i32, B as Bytes32Value, C as ValueInput, D as dec, E as bytes32, F as u64, G as StrValue, H as DecValue, I as AddrValue, J as U64Value, K as TypeTag, L as AnyArkivValue, M as str, N as toValue, O as decFromUnits, P as u256, R as ArkivValue, S as U64_MAX, T as bool, U as I32Value, V as BytesValue, W as KeyValue, X as isArkivValue, Y as UserTypeTag, _ as MAX_PAYLOAD_BYTES, a as InvalidAttributeNameError, b as MAX_STRING_BYTES, c as TooManyAttributesError, d as DECIMAL_SCALE, f as DECIMAL_UNIT, g as MAX_ATTRIBUTES, h as Attributes, i as ConflictingMutationError, j as key, k as decUnits, l as UnknownAttributeTypeError, m as AttributeSchema, n as isValidAttributeName, o as InvalidValueError, p as AttributeInputs, q as U256Value, r as validateAttributeName, s as MissingValueError, t as MAX_NAME_BYTES, u as UntypedValueError, v as I32_MAX, w as addr, x as U256_MAX, y as I32_MIN, z as BoolValue } from "../index-CIcHPx2j.cjs";
2
- export { AddrValue, AnyArkivValue, ArkivValue, AttributeInputs, AttributeSchema, Attributes, BoolValue, Bytes32Value, BytesValue, ConflictingMutationError, DECIMAL_SCALE, DECIMAL_UNIT, DecValue, I32Value, I32_MAX, I32_MIN, InvalidAttributeNameError, InvalidValueError, KeyValue, MAX_ATTRIBUTES, MAX_NAME_BYTES, MAX_PAYLOAD_BYTES, MAX_STRING_BYTES, MissingValueError, StrValue, TooManyAttributesError, TypeTag, U256Value, U256_MAX, U64Value, U64_MAX, UnknownAttributeTypeError, UntypedValueError, UserTypeTag, ValueInput, addr, bool, bytes32, dec, decFromUnits, decUnits, i32, isArkivValue, isValidAttributeName, key, str, toValue, u256, u64, validateAttributeName };
@@ -1,2 +0,0 @@
1
- import { A as i32, B as Bytes32Value, C as ValueInput, D as dec, E as bytes32, F as u64, G as StrValue, H as DecValue, I as AddrValue, J as U64Value, K as TypeTag, L as AnyArkivValue, M as str, N as toValue, O as decFromUnits, P as u256, R as ArkivValue, S as U64_MAX, T as bool, U as I32Value, V as BytesValue, W as KeyValue, X as isArkivValue, Y as UserTypeTag, _ as MAX_PAYLOAD_BYTES, a as InvalidAttributeNameError, b as MAX_STRING_BYTES, c as TooManyAttributesError, d as DECIMAL_SCALE, f as DECIMAL_UNIT, g as MAX_ATTRIBUTES, h as Attributes, i as ConflictingMutationError, j as key, k as decUnits, l as UnknownAttributeTypeError, m as AttributeSchema, n as isValidAttributeName, o as InvalidValueError, p as AttributeInputs, q as U256Value, r as validateAttributeName, s as MissingValueError, t as MAX_NAME_BYTES, u as UntypedValueError, v as I32_MAX, w as addr, x as U256_MAX, y as I32_MIN, z as BoolValue } from "../index-Cd2aaWme.js";
2
- export { AddrValue, AnyArkivValue, ArkivValue, AttributeInputs, AttributeSchema, Attributes, BoolValue, Bytes32Value, BytesValue, ConflictingMutationError, DECIMAL_SCALE, DECIMAL_UNIT, DecValue, I32Value, I32_MAX, I32_MIN, InvalidAttributeNameError, InvalidValueError, KeyValue, MAX_ATTRIBUTES, MAX_NAME_BYTES, MAX_PAYLOAD_BYTES, MAX_STRING_BYTES, MissingValueError, StrValue, TooManyAttributesError, TypeTag, U256Value, U256_MAX, U64Value, U64_MAX, UnknownAttributeTypeError, UntypedValueError, UserTypeTag, ValueInput, addr, bool, bytes32, dec, decFromUnits, decUnits, i32, isArkivValue, isValidAttributeName, key, str, toValue, u256, u64, validateAttributeName };
@@ -1,3 +0,0 @@
1
- import { A as DECIMAL_UNIT, C as str, E as u64, F as TooManyAttributesError, I as UnknownAttributeTypeError, L as UntypedValueError, M as InvalidAttributeNameError, N as InvalidValueError, O as isArkivValue, P as MissingValueError, S as key, T as u256, _ as bytes32, a as MAX_NAME_BYTES, b as decUnits, d as I32_MIN, f as MAX_STRING_BYTES, g as bool, h as addr, j as ConflictingMutationError, k as DECIMAL_SCALE, m as U64_MAX, n as MAX_PAYLOAD_BYTES, o as isValidAttributeName, p as U256_MAX, s as validateAttributeName, t as MAX_ATTRIBUTES, u as I32_MAX, v as dec, w as toValue, x as i32, y as decFromUnits } from "../attr-tmyREHtw.js";
2
-
3
- export { ConflictingMutationError, DECIMAL_SCALE, DECIMAL_UNIT, I32_MAX, I32_MIN, InvalidAttributeNameError, InvalidValueError, MAX_ATTRIBUTES, MAX_NAME_BYTES, MAX_PAYLOAD_BYTES, MAX_STRING_BYTES, MissingValueError, TooManyAttributesError, U256_MAX, U64_MAX, UnknownAttributeTypeError, UntypedValueError, addr, bool, bytes32, dec, decFromUnits, decUnits, i32, isArkivValue, isValidAttributeName, key, str, toValue, u256, u64, validateAttributeName };