@arkiv-network/sdk 0.6.8 → 0.7.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 (101) hide show
  1. package/README.md +52 -16
  2. package/dist/consts-Bz0LZQZY.js +7 -0
  3. package/dist/consts-Bz0LZQZY.js.map +1 -0
  4. package/dist/consts-CxT1-RXU.cjs +18 -0
  5. package/dist/entity-BhSXaB23.cjs +81 -0
  6. package/dist/{query-CO6f35AE.d.cts → entity-CN41IOrq.d.cts} +125 -59
  7. package/dist/entity-CN41IOrq.d.cts.map +1 -0
  8. package/dist/entity-DGfVyB-a.js +71 -0
  9. package/dist/entity-DGfVyB-a.js.map +1 -0
  10. package/dist/{query-BhdmAzYg.d.ts → entity-NnEVlePN.d.ts} +125 -59
  11. package/dist/entity-NnEVlePN.d.ts.map +1 -0
  12. package/dist/index-ClkpS6s_.d.ts +481 -0
  13. package/dist/index-ClkpS6s_.d.ts.map +1 -0
  14. package/dist/index-w4qop0Pe.d.cts +481 -0
  15. package/dist/index-w4qop0Pe.d.cts.map +1 -0
  16. package/dist/index.cjs +285 -233
  17. package/dist/index.d.cts +100 -6
  18. package/dist/index.d.cts.map +1 -1
  19. package/dist/index.d.ts +101 -7
  20. package/dist/index.d.ts.map +1 -1
  21. package/dist/index.js +280 -230
  22. package/dist/index.js.map +1 -1
  23. package/dist/payload-BpNUPOnY.js +13 -0
  24. package/dist/payload-BpNUPOnY.js.map +1 -0
  25. package/dist/{payload-Da9gJMG5.d.ts → payload-C2KHOUoj.d.ts} +1 -1
  26. package/dist/{payload-Da9gJMG5.d.ts.map → payload-C2KHOUoj.d.ts.map} +1 -1
  27. package/dist/{payload-vwU3Rc6P.cjs → payload-D8YLKcJj.cjs} +1 -18
  28. package/dist/query/index.cjs +11 -28
  29. package/dist/query/index.d.cts +3 -3
  30. package/dist/query/index.d.ts +3 -3
  31. package/dist/query/index.js +8 -28
  32. package/dist/query/index.js.map +1 -1
  33. package/dist/{queryBuilder-DonAg1aq.js → queryBuilder-BE_XhLLa.js} +303 -152
  34. package/dist/queryBuilder-BE_XhLLa.js.map +1 -0
  35. package/dist/{queryBuilder-DM-YO1Y7.cjs → queryBuilder-J37buGCi.cjs} +372 -149
  36. package/dist/types/index.cjs +4 -1
  37. package/dist/types/index.d.cts +2 -2
  38. package/dist/types/index.d.ts +2 -2
  39. package/dist/types/index.js +4 -1
  40. package/dist/utils/index.cjs +4 -10
  41. package/dist/utils/index.d.cts +0 -1
  42. package/dist/utils/index.d.cts.map +1 -1
  43. package/dist/utils/index.d.ts +1 -2
  44. package/dist/utils/index.d.ts.map +1 -1
  45. package/dist/utils/index.js +2 -3
  46. package/dist/utils/index.js.map +1 -1
  47. package/package.json +11 -15
  48. package/src/actions/public/buildQuery.ts +4 -0
  49. package/src/actions/public/query.ts +10 -0
  50. package/src/actions/public/subscribeEntityEvents.ts +25 -51
  51. package/src/actions/wallet/changeOwnership.ts +6 -6
  52. package/src/actions/wallet/createEntity.ts +19 -6
  53. package/src/actions/wallet/deleteEntity.ts +2 -3
  54. package/src/actions/wallet/extendEntity.ts +9 -5
  55. package/src/actions/wallet/mutateEntities.ts +11 -54
  56. package/src/actions/wallet/updateEntity.ts +15 -6
  57. package/src/clients/createPublicClient.test.ts +2 -0
  58. package/src/clients/decorators/arkivPublic.ts +67 -2
  59. package/src/clients/decorators/arkivWallet.ts +23 -1
  60. package/src/consts.ts +2 -1
  61. package/src/errors.ts +46 -0
  62. package/src/index.ts +0 -3
  63. package/src/query/engine.test.ts +45 -0
  64. package/src/query/engine.ts +28 -14
  65. package/src/query/index.ts +1 -0
  66. package/src/query/predicate.test.ts +43 -0
  67. package/src/query/predicate.ts +14 -10
  68. package/src/query/queryBuilder.test.ts +22 -0
  69. package/src/query/queryBuilder.ts +221 -106
  70. package/src/query/queryResult.ts +13 -7
  71. package/src/query/selectQueryBuilder.test.ts +213 -0
  72. package/src/query/selection.test.ts +120 -0
  73. package/src/query/selection.ts +116 -0
  74. package/src/types/attributes.ts +25 -1
  75. package/src/types/entity.ts +12 -2
  76. package/src/types/events.ts +14 -16
  77. package/src/types/index.ts +1 -0
  78. package/src/types/rpcSchema.ts +7 -2
  79. package/src/utils/arkivTransactions.test.ts +260 -0
  80. package/src/utils/arkivTransactions.ts +255 -92
  81. package/src/utils/entities.ts +17 -7
  82. package/src/utils/index.ts +0 -1
  83. package/src/utils/payload.ts +4 -2
  84. package/src/utils/validation.test.ts +106 -0
  85. package/src/utils/validation.ts +80 -0
  86. package/dist/accounts/index.cjs +0 -9
  87. package/dist/accounts/index.d.cts +0 -1
  88. package/dist/accounts/index.d.ts +0 -1
  89. package/dist/accounts/index.js +0 -1
  90. package/dist/payload-DKBYYjeA.js +0 -18
  91. package/dist/payload-DKBYYjeA.js.map +0 -1
  92. package/dist/query-BhdmAzYg.d.ts.map +0 -1
  93. package/dist/query-CO6f35AE.d.cts.map +0 -1
  94. package/dist/queryBuilder-BvoQXa_u.d.ts +0 -328
  95. package/dist/queryBuilder-BvoQXa_u.d.ts.map +0 -1
  96. package/dist/queryBuilder-DonAg1aq.js.map +0 -1
  97. package/dist/queryBuilder-zTsKXctT.d.cts +0 -328
  98. package/dist/queryBuilder-zTsKXctT.d.cts.map +0 -1
  99. package/src/accounts/index.ts +0 -1
  100. /package/dist/{types-H-TSmRpm.js → types-Ci4BRClw.js} +0 -0
  101. /package/dist/{types-o3zWarRp.cjs → types-a8gm_IaQ.cjs} +0 -0
@@ -1,4 +1,6 @@
1
- import { bytesToString, hexToNumber, numberToHex, toBytes } from "viem";
1
+ import { n as BLOCK_TIME } from "./consts-Bz0LZQZY.js";
2
+ import { t as Entity } from "./entity-DGfVyB-a.js";
3
+ import { hexToNumber, isHex, numberToHex, toBytes } from "viem";
2
4
 
3
5
  //#region src/utils/logger.ts
4
6
  const ROOT_NAMESPACE = "arkiv";
@@ -120,6 +122,31 @@ var EntityMutationError = class extends Error {
120
122
  this.name = "EntityMutationError";
121
123
  }
122
124
  };
125
+ var InvalidExpirationError = class extends Error {
126
+ constructor(expiresIn) {
127
+ super(`Invalid expiresIn: ${String(expiresIn)}. expiresIn must be a positive integer and a multiple of the Arkiv block time (${BLOCK_TIME} seconds), because expiration is measured in whole blocks (1 block = ${BLOCK_TIME} seconds).`);
128
+ this.name = "InvalidExpirationError";
129
+ }
130
+ };
131
+ var InvalidAttributeError = class extends Error {
132
+ constructor(key, value) {
133
+ const remedy = Number.isInteger(value) ? `Offset it into the non-negative range (e.g. store ${String(value)} + OFFSET, subtracting OFFSET on read) to keep numeric ordering/range queries; otherwise store it as a string (e.g. "${String(value)}")` : `To keep numeric ordering/range queries, scale it to an integer (e.g. Math.round(${String(value)} * 1000), dividing by 1000 on read); otherwise store it as a string (e.g. "${String(value)}")`;
134
+ super(`Invalid numeric value for attribute "${key}": ${String(value)}. Numeric attribute values must be non-negative integers (stored on-chain as unsigned 256-bit). ${remedy}.`);
135
+ this.name = "InvalidAttributeError";
136
+ }
137
+ };
138
+ var InvalidAttributeKeyError = class extends Error {
139
+ constructor(key, reason) {
140
+ super(`Invalid attribute key "${key}": ${reason}. Keys must be 1-32 bytes, start with a lowercase letter (a-z), and contain only a-z, 0-9, ".", "-" or "_".`);
141
+ this.name = "InvalidAttributeKeyError";
142
+ }
143
+ };
144
+ var DuplicateAttributeError = class extends Error {
145
+ constructor(key) {
146
+ super(`Duplicate attribute key "${key}". Attribute keys must be unique per entity.`);
147
+ this.name = "DuplicateAttributeError";
148
+ }
149
+ };
123
150
  var NoMoreResultsError = class extends Error {
124
151
  constructor() {
125
152
  super("No more results");
@@ -138,85 +165,102 @@ var NoEntityFoundError = class extends Error {
138
165
  this.name = "NoEntityFoundError";
139
166
  }
140
167
  };
141
-
142
- //#endregion
143
- //#region src/types/entity.ts
144
- var Entity = class {
145
- key;
146
- contentType;
147
- owner;
148
- creator;
149
- expiresAtBlock;
150
- createdAtBlock;
151
- lastModifiedAtBlock;
152
- transactionIndexInBlock;
153
- operationIndexInTransaction;
154
- payload;
155
- attributes;
156
- constructor(key, contentType = void 0, owner = void 0, creator = void 0, expiresAtBlock = void 0, createdAtBlock = void 0, lastModifiedAtBlock = void 0, transactionIndexInBlock = void 0, operationIndexInTransaction = void 0, payload = void 0, attributes) {
157
- this.key = key;
158
- this.owner = owner;
159
- this.creator = creator;
160
- this.expiresAtBlock = expiresAtBlock;
161
- this.createdAtBlock = createdAtBlock;
162
- this.lastModifiedAtBlock = lastModifiedAtBlock;
163
- this.transactionIndexInBlock = transactionIndexInBlock;
164
- this.operationIndexInTransaction = operationIndexInTransaction;
165
- this.payload = payload;
166
- this.attributes = attributes;
167
- this.contentType = contentType;
168
- }
169
- /**
170
- * Converts the entity payload from bytes to a string and returns it.
171
- * Throws an error if the payload is undefined, which may occur if the entity was not queried with the withPayload option.
172
- * Throws an error if the conversion from bytes to string fails.
173
- * @returns The entity payload as a string.
174
- */
175
- toText() {
176
- if (this.payload === void 0) throw new Error("Entity has no payload – it was probably queried without withPayload(true) via QueryBuilder");
177
- try {
178
- return bytesToString(this.payload);
179
- } catch (e) {
180
- throw new Error(`Failed to convert entity payload to text: ${e instanceof Error ? e.message : String(e)}`, { cause: e });
181
- }
182
- }
183
- /**
184
- * Parses the entity payload as JSON and returns the resulting object.
185
- * Throws an error if the payload is undefined, which may occur if the entity was not queried with the withPayload option.
186
- * Throws an error if the payload is empty or cannot be parsed as JSON.
187
- * @returns The parsed JSON object from the entity payload.
188
- */
189
- toJson() {
190
- const text = this.toText();
191
- if (!text) throw new Error("Entity has empty payload, cannot parse as JSON");
192
- try {
193
- return JSON.parse(text);
194
- } catch (e) {
195
- throw new Error(`Failed to parse entity payload as JSON: ${e instanceof Error ? e.message : String(e)}`, { cause: e });
196
- }
168
+ var InvalidContentTypeError = class extends Error {
169
+ constructor(contentType) {
170
+ super(`Invalid content type "${contentType}". Must follow RFC 2045 MIME grammar and be lowercase only (e.g. "text/plain").`);
171
+ this.name = "InvalidContentTypeError";
197
172
  }
198
173
  };
199
174
 
175
+ //#endregion
176
+ //#region src/types/rpcSchema.ts
177
+ let RpcAttributeValueType = /* @__PURE__ */ function(RpcAttributeValueType$1) {
178
+ RpcAttributeValueType$1[RpcAttributeValueType$1["Uint"] = 1] = "Uint";
179
+ RpcAttributeValueType$1[RpcAttributeValueType$1["String"] = 2] = "String";
180
+ RpcAttributeValueType$1[RpcAttributeValueType$1["EntityKey"] = 3] = "EntityKey";
181
+ return RpcAttributeValueType$1;
182
+ }({});
183
+
200
184
  //#endregion
201
185
  //#region src/utils/entities.ts
186
+ function decodeAttributeValue(value, valueType) {
187
+ switch (valueType) {
188
+ case RpcAttributeValueType.Uint: return Number(BigInt(value));
189
+ case RpcAttributeValueType.EntityKey: return value;
190
+ case RpcAttributeValueType.String:
191
+ default: return value;
192
+ }
193
+ }
202
194
  const logger$2 = getLogger("utils:entities");
203
195
  async function entityFromRpcResult(rpcEntity) {
204
196
  logger$2("entityFromRpcResult %o", rpcEntity);
205
- return new Entity(rpcEntity.key, rpcEntity.contentType, rpcEntity.owner, rpcEntity.creator, rpcEntity.expiresAt !== void 0 ? BigInt(rpcEntity.expiresAt) : void 0, rpcEntity.createdAtBlock !== void 0 ? BigInt(rpcEntity.createdAtBlock) : void 0, rpcEntity.lastModifiedAtBlock !== void 0 ? BigInt(rpcEntity.lastModifiedAtBlock) : void 0, rpcEntity.transactionIndexInBlock !== void 0 ? BigInt(rpcEntity.transactionIndexInBlock) : void 0, rpcEntity.operationIndexInTransaction !== void 0 ? BigInt(rpcEntity.operationIndexInTransaction) : void 0, rpcEntity.value !== void 0 ? toBytes(rpcEntity.value) : void 0, [...(rpcEntity.stringAttributes ?? []).map(({ key, value }) => ({
206
- key,
207
- value
208
- })), ...(rpcEntity.numericAttributes ?? []).map(({ key, value }) => ({
197
+ return new Entity(rpcEntity.key, rpcEntity.contentType, rpcEntity.owner, rpcEntity.creator, rpcEntity.expiresAt !== void 0 ? BigInt(rpcEntity.expiresAt) : void 0, rpcEntity.createdAtBlock !== void 0 ? BigInt(rpcEntity.createdAtBlock) : void 0, rpcEntity.lastModifiedAtBlock !== void 0 ? BigInt(rpcEntity.lastModifiedAtBlock) : void 0, rpcEntity.transactionIndexInBlock !== void 0 ? BigInt(rpcEntity.transactionIndexInBlock) : void 0, rpcEntity.operationIndexInTransaction !== void 0 ? BigInt(rpcEntity.operationIndexInTransaction) : void 0, rpcEntity.value !== void 0 ? toBytes(rpcEntity.value) : void 0, [...(rpcEntity.attributes ?? []).map(({ key, value, valueType }) => ({
209
198
  key,
210
- value: Number(value)
199
+ value: decodeAttributeValue(value, valueType)
211
200
  }))]);
212
201
  }
213
202
 
203
+ //#endregion
204
+ //#region src/utils/validation.ts
205
+ /**
206
+ * True only for hex strings that are exactly 32 bytes (an entity key). This is
207
+ * the single rule deciding whether an attribute string value is stored as the
208
+ * on-chain `EntityKey` type or as a plain string — the query engine applies
209
+ * the same rule, so equality comparisons agree with storage on both paths.
210
+ * Hex-looking strings of any other length are treated as ordinary strings.
211
+ */
212
+ function isEntityKey(value) {
213
+ return isHex(value) && value.length === 66;
214
+ }
215
+ /**
216
+ * Expiration on Arkiv is measured in blocks (1 block =
217
+ * 2 seconds), so the value must be a positive integer that is
218
+ * a multiple of the block time.
219
+ *
220
+ * @param expiresIn - The expiration in seconds.
221
+ * @throws {InvalidExpirationError} If the value is not a positive integer that
222
+ * is a multiple of the block time.
223
+ */
224
+ function validateExpiresIn(expiresIn) {
225
+ if (!Number.isInteger(expiresIn) || expiresIn <= 0 || expiresIn % BLOCK_TIME !== 0) throw new InvalidExpirationError(expiresIn);
226
+ }
227
+ /**
228
+ * Validates an attribute key against the on-chain `Ident32` rules (mirrors
229
+ * `Ident32.sol` / `validate_ident32_bytes` in the node): non-empty, at most
230
+ * 32 bytes, leading character `a-z`, remaining characters `a-z 0-9 . - _`.
231
+ *
232
+ * @param key - The attribute key to validate.
233
+ * @throws {InvalidAttributeKeyError} If the key violates the `Ident32` rules.
234
+ */
235
+ function validateAttributeKey(key) {
236
+ if (key.length === 0) throw new InvalidAttributeKeyError(key, "key is empty");
237
+ if (!/^[a-z]/.test(key)) throw new InvalidAttributeKeyError(key, `key starts with "${key[0]}"`);
238
+ const invalid = key.match(/[^a-z0-9._-]/);
239
+ if (invalid) throw new InvalidAttributeKeyError(key, `key contains "${invalid[0]}" at position ${invalid.index}`);
240
+ if (key.length > 32) throw new InvalidAttributeKeyError(key, `key is ${key.length} bytes long`);
241
+ }
242
+ /**
243
+ * Validates a single attribute. Keys must follow the on-chain `Ident32` rules
244
+ * (see {@link validateAttributeKey}). Arkiv supports string and number
245
+ * attribute values, but numeric values must be non-negative integers — they
246
+ * are stored on-chain as unsigned 256-bit values.
247
+ *
248
+ * @param attribute - The attribute to validate.
249
+ * @throws {InvalidAttributeKeyError} If the key violates the `Ident32` rules.
250
+ * @throws {InvalidAttributeError} If the attribute has a non-integer or
251
+ * negative numeric value.
252
+ */
253
+ function validateAttribute(attribute) {
254
+ validateAttributeKey(attribute.key);
255
+ if (typeof attribute.value === "number" && (!Number.isInteger(attribute.value) || attribute.value < 0)) throw new InvalidAttributeError(attribute.key, attribute.value);
256
+ }
257
+
214
258
  //#endregion
215
259
  //#region src/query/engine.ts
216
260
  const logger$1 = getLogger("query:engine");
217
261
  function processPredicates(predicates) {
218
262
  const processValue = (value) => {
219
- if (typeof value === "string") return `"${value}"`;
263
+ if (typeof value === "string") return isEntityKey(value) ? value : `"${value}"`;
220
264
  return value;
221
265
  };
222
266
  return predicates.map((predicate) => {
@@ -235,7 +279,7 @@ function processPredicates(predicates) {
235
279
  }).join(" && ");
236
280
  }
237
281
  async function processQuery(client, queryParams) {
238
- const { predicates, limit, cursor, ownedBy, createdBy, orderBy, validAtBlock, withAttributes, withMetadata, withPayload } = queryParams;
282
+ const { predicates, limit, cursor, ownedBy, createdBy, orderBy, validAtBlock, withAttributes, withMetadata, withPayload, includeData } = queryParams;
239
283
  logger$1("Processing query with params %o", {
240
284
  predicates,
241
285
  cursor,
@@ -246,14 +290,15 @@ async function processQuery(client, queryParams) {
246
290
  validAtBlock,
247
291
  withAttributes,
248
292
  withMetadata,
249
- withPayload
293
+ withPayload,
294
+ includeData
250
295
  });
251
296
  let query = processPredicates(predicates);
252
297
  if (ownedBy) query += ` && $owner=${ownedBy}`;
253
298
  if (createdBy) query += ` && $creator=${createdBy}`;
254
299
  query = query.trim();
255
300
  if (query.startsWith("&& ")) query = query.slice(3);
256
- const queryOptions = { includeData: {
301
+ const queryOptions = { includeData: includeData ?? {
257
302
  key: true,
258
303
  attributes: withAttributes ?? false,
259
304
  payload: withPayload ?? false,
@@ -288,6 +333,12 @@ async function processQuery(client, queryParams) {
288
333
  //#endregion
289
334
  //#region src/query/queryResult.ts
290
335
  const logger = getLogger("query:result");
336
+ /**
337
+ * The result of a query. Holds the fetched entities and supports cursor-based pagination.
338
+ *
339
+ * @typeParam TEntity - The shape of each entity, inferred from the query builder
340
+ * (a full {@link Entity}, or a projected object inferred from a `select()` selection).
341
+ */
291
342
  var QueryResult = class {
292
343
  entities;
293
344
  _endOfIteration;
@@ -325,6 +376,53 @@ var QueryResult = class {
325
376
  }
326
377
  };
327
378
 
379
+ //#endregion
380
+ //#region src/query/selection.ts
381
+ /**
382
+ * Converts a {@link SelectArg} into the {@link RpcIncludeData} understood by the query engine.
383
+ * Every field is opt-in — anything not explicitly selected defaults to `false`, including the key.
384
+ * `undefined` or `"*"` selects everything.
385
+ *
386
+ * @param selection - The selection to convert
387
+ * @returns The fully-resolved include-data object for the RPC query
388
+ * @throws If an empty selection object is provided
389
+ *
390
+ * @example
391
+ * selectionToIncludeData() // everything
392
+ * selectionToIncludeData("*") // everything
393
+ * selectionToIncludeData({ key: true }) // only the key
394
+ * selectionToIncludeData({ owner: true }) // only the owner
395
+ */
396
+ function selectionToIncludeData(selection) {
397
+ if (selection === void 0 || selection === "*") return {
398
+ key: true,
399
+ attributes: true,
400
+ payload: true,
401
+ contentType: true,
402
+ expiration: true,
403
+ owner: true,
404
+ creator: true,
405
+ createdAtBlock: true,
406
+ lastModifiedAtBlock: true,
407
+ transactionIndexInBlock: true,
408
+ operationIndexInTransaction: true
409
+ };
410
+ if (!Object.values(selection).some((selected) => selected === true)) throw new Error("select() requires at least one field to be selected. Pass select() or select(\"*\") to select everything.");
411
+ return {
412
+ key: selection.key ?? false,
413
+ attributes: selection.attributes ?? false,
414
+ payload: selection.payload ?? false,
415
+ contentType: selection.contentType ?? false,
416
+ expiration: selection.expiresAtBlock ?? false,
417
+ owner: selection.owner ?? false,
418
+ creator: selection.creator ?? false,
419
+ createdAtBlock: selection.createdAtBlock ?? false,
420
+ lastModifiedAtBlock: selection.lastModifiedAtBlock ?? false,
421
+ transactionIndexInBlock: selection.transactionIndexInBlock ?? false,
422
+ operationIndexInTransaction: selection.operationIndexInTransaction ?? false
423
+ };
424
+ }
425
+
328
426
  //#endregion
329
427
  //#region src/query/queryBuilder.ts
330
428
  /**
@@ -333,6 +431,10 @@ var QueryResult = class {
333
431
  * @param attributeType - The type of the attribute to order by (string or number)
334
432
  * @returns Input for orderBy method
335
433
  *
434
+ * @deprecated Server-side ordering is not supported by the network, so `orderBy` (and this
435
+ * helper) have no effect on the returned order. Sort the fetched entities in JavaScript
436
+ * instead (e.g. `entities.sort(...)`). This function will be removed in a future release.
437
+ *
336
438
  * @example
337
439
  * const ascAttribute = asc("name", "string")
338
440
  */
@@ -349,6 +451,10 @@ function asc(attributeName, attributeType) {
349
451
  * @param attributeType - The type of the attribute to order by (string or number)
350
452
  * @returns Input for orderBy method
351
453
  *
454
+ * @deprecated Server-side ordering is not supported by the network, so `orderBy` (and this
455
+ * helper) have no effect on the returned order. Sort the fetched entities in JavaScript
456
+ * instead (e.g. `entities.sort(...)`). This function will be removed in a future release.
457
+ *
352
458
  * @example
353
459
  * const descAttribute = desc("name", "string")
354
460
  */
@@ -360,20 +466,21 @@ function desc(attributeName, attributeType) {
360
466
  };
361
467
  }
362
468
  /**
363
- * QueryBuilder is a helper class to build queries to the Arkiv DBChains.
364
- * It can be used to fetch entities from the Arkiv DBChains. It follows the Builder pattern allowing chaining of methods.
365
- * @param client - The Arkiv client
366
- * @returns The QueryBuilder instance {@link QueryBuilder}
469
+ * BaseQueryBuilder holds the query-building logic shared by every query builder
470
+ * (filtering, ordering, pagination, execution). It follows the Builder pattern,
471
+ * allowing methods to be chained. Subclasses decide how data selection is expressed
472
+ * by implementing the protected `selectionParams` method.
473
+ *
474
+ * Use {@link SelectQueryBuilder} via `client.select()` to build and execute queries.
475
+ *
476
+ * @typeParam TEntity - The shape of each entity produced by {@link BaseQueryBuilder.fetch}.
367
477
  */
368
- var QueryBuilder = class {
478
+ var BaseQueryBuilder = class {
369
479
  _client;
370
480
  _ownedBy;
371
481
  _createdBy;
372
482
  _orderBy;
373
483
  _validAtBlock;
374
- _withAttributes;
375
- _withMetadata;
376
- _withPayload;
377
484
  _limit;
378
485
  _cursor;
379
486
  _predicates;
@@ -384,10 +491,10 @@ var QueryBuilder = class {
384
491
  /**
385
492
  * Sets the ownedBy filter
386
493
  * @param ownedBy - The address of the owner
387
- * @returns The QueryBuilder instance
494
+ * @returns The query builder instance
388
495
  *
389
496
  * @example
390
- * const builder = new QueryBuilder(client)
497
+ * const builder = client.select()
391
498
  * builder.ownedBy("0x1234567890123456789012345678901234567890")
392
499
  */
393
500
  ownedBy(ownedBy) {
@@ -397,10 +504,10 @@ var QueryBuilder = class {
397
504
  /**
398
505
  * Sets the createdBy filter
399
506
  * @param createdBy - The address of the creator
400
- * @returns The QueryBuilder instance
507
+ * @returns The query builder instance
401
508
  *
402
509
  * @example
403
- * const builder = new QueryBuilder(client)
510
+ * const builder = client.select()
404
511
  * builder.createdBy("0x1234567890123456789012345678901234567890")
405
512
  */
406
513
  createdBy(createdBy) {
@@ -427,51 +534,12 @@ var QueryBuilder = class {
427
534
  return this;
428
535
  }
429
536
  /**
430
- * Sets the withAttributes flag which will return the attributes for the entities if true
431
- * @param withAttributes - The boolean value to set
432
- * @returns The QueryBuilder instance
433
- *
434
- * @example
435
- * const builder = new QueryBuilder(client)
436
- * builder.withAttributes(true)
437
- */
438
- withAttributes(withAttributes = true) {
439
- this._withAttributes = withAttributes;
440
- return this;
441
- }
442
- /**
443
- * Sets the withMetadata flag which will return the metadata (like owner, expiredAt, etc.) for the entities if true
444
- * @param withMetadata - The boolean value to set
445
- * @returns The QueryBuilder instance
446
- *
447
- * @example
448
- * const builder = new QueryBuilder(client)
449
- * builder.withMetadata(true)
450
- */
451
- withMetadata(withMetadata = true) {
452
- this._withMetadata = withMetadata;
453
- return this;
454
- }
455
- /**
456
- * Sets the withPayload flag which will return the payload for the entities if true
457
- * @param withPayload - The boolean value to set
458
- * @returns The QueryBuilder instance
459
- *
460
- * @example
461
- * const builder = new QueryBuilder(client)
462
- * builder.withPayload(true)
463
- */
464
- withPayload(withPayload = true) {
465
- this._withPayload = withPayload;
466
- return this;
467
- }
468
- /**
469
537
  * Sets the limit for the query
470
538
  * @param limit - The number of entities to return
471
- * @returns The QueryBuilder instance
539
+ * @returns The query builder instance
472
540
  *
473
541
  * @example
474
- * const builder = new QueryBuilder(client)
542
+ * const builder = client.select()
475
543
  * builder.limit(10)
476
544
  */
477
545
  limit(limit) {
@@ -481,11 +549,11 @@ var QueryBuilder = class {
481
549
  /**
482
550
  * Sets the cursor for the query - it is advances setting which rather shouldn't be used manually but it is provided from query result if limit is used (pagination).
483
551
  * @param cursor - The cursor to set which tells to RPC Query server where to start or continue the query.
484
- * @returns The QueryBuilder instance
552
+ * @returns The query builder instance
485
553
  *
486
554
  * @example
487
- * const builder = new QueryBuilder(client)
488
- * builder.offset(10)
555
+ * const builder = client.select()
556
+ * builder.cursor("0xABC123")
489
557
  */
490
558
  cursor(cursor) {
491
559
  this._cursor = cursor;
@@ -495,44 +563,27 @@ var QueryBuilder = class {
495
563
  * Sets the validAtBlock for the query which tells at which block height the state we are intested.
496
564
  * If not set, the latest block is used.
497
565
  * @param validAtBlock - The block number to set
498
- * @returns The QueryBuilder instance
566
+ * @returns The query builder instance
499
567
  *
500
568
  * @example
501
- * const builder = new QueryBuilder(client)
569
+ * const builder = client.select()
502
570
  * builder.validAtBlock(10000)
503
571
  */
504
572
  validAtBlock(validAtBlock) {
505
573
  this._validAtBlock = validAtBlock;
506
574
  return this;
507
575
  }
508
- /**
509
- * Sets the predicates for the query limiting the results. It can be a single predicate or an array of predicates combined with 'and'.
510
- * Predicates can be nested using 'or' and 'and' predicates.
511
- * @param predicates - The predicates to set
512
- * @returns The QueryBuilder instance
513
- *
514
- * @example
515
- * const builder = new QueryBuilder(client)
516
- * builder.where(eq("name", "John"))
517
- * builder.where([eq("name", "John"), eq("age", 30)])
518
- * builder.where([eq("name", "John"), or([eq("age", 30), eq("age", 31)])])
519
- * builder.where([eq("name", "John"), and([eq("age", 30), eq("age", 31)])])
520
- * builder.where([eq("name", "John"), or([eq("age", 30), and([eq("age", 31), eq("age", 32)])])])
521
- * builder.where([eq("name", "John"), and([eq("age", 30), or([eq("age", 31), eq("age", 32)])])])
522
- * builder.where([eq("name", "John"), and([eq("age", 30), or([eq("age", 31), and([eq("age", 32), eq("age", 33)])])])])
523
- */
524
- where(predicates) {
525
- if (Array.isArray(predicates)) this._predicates.push(...predicates);
526
- else this._predicates.push(predicates);
576
+ where(...predicates) {
577
+ this._predicates.push(...predicates.flat());
527
578
  return this;
528
579
  }
529
580
  /**
530
- * Fetches the entities from the query. Re
581
+ * Fetches the entities from the query.
531
582
  * It will return a QueryResult instance which can be used to fetch the next and previous pages.
532
583
  * @returns The QueryResult instance {@link QueryResult}
533
584
  *
534
585
  * @example
535
- * const builder = new QueryBuilder(client)
586
+ * const builder = client.select()
536
587
  * const result = await builder.where(eq("name", "John")).fetch()
537
588
  * // result = { entities: [Entity, Entity, Entity], next: async () => QueryResult, previous: async () => QueryResult }
538
589
  */
@@ -545,11 +596,9 @@ var QueryBuilder = class {
545
596
  createdBy: this._createdBy,
546
597
  orderBy: this._orderBy,
547
598
  validAtBlock: this._validAtBlock,
548
- withAttributes: this._withAttributes,
549
- withMetadata: this._withMetadata,
550
- withPayload: this._withPayload
599
+ ...this.selectionParams()
551
600
  });
552
- const entities = await Promise.all(queryResult.data.map((entity) => entityFromRpcResult(entity)));
601
+ const entities = await Promise.all(queryResult.data.map((entity) => this.projectEntity(entity)));
553
602
  this.cursor(queryResult.cursor);
554
603
  this.validAtBlock(BigInt(queryResult.blockNumber ?? 0));
555
604
  return new QueryResult(entities, this, this._cursor, this._limit, this._validAtBlock);
@@ -559,7 +608,7 @@ var QueryBuilder = class {
559
608
  * @returns The number of entities
560
609
  *
561
610
  * @example
562
- * const builder = new QueryBuilder(client)
611
+ * const builder = client.select()
563
612
  * const result = await builder.where(eq("name", "John")).count()
564
613
  * // result = 10
565
614
  */
@@ -578,7 +627,109 @@ var QueryBuilder = class {
578
627
  })).data.length ?? 0;
579
628
  }
580
629
  };
630
+ /**
631
+ * QueryBuilder is a helper class to build queries to the Arkiv DBChains.
632
+ * It can be used to fetch entities from the Arkiv DBChains. It follows the Builder pattern allowing chaining of methods.
633
+ *
634
+ * By default the result includes only the entity `key`. Additional data is opt-in through
635
+ * `withAttributes()`, `withMetadata()` and `withPayload()`.
636
+ *
637
+ * @deprecated Use {@link SelectQueryBuilder} via `client.select()` instead. Declaring the
638
+ * selection up front avoids the common mistake of forgetting to opt in to data and getting back
639
+ * entities with only their `key` populated. This class remains for backwards compatibility and
640
+ * will be removed in a future release.
641
+ *
642
+ * @param client - The Arkiv client
643
+ * @returns The QueryBuilder instance {@link QueryBuilder}
644
+ */
645
+ var QueryBuilder = class extends BaseQueryBuilder {
646
+ _withAttributes;
647
+ _withMetadata;
648
+ _withPayload;
649
+ /**
650
+ * Sets the withAttributes flag which will return the attributes for the entities if true
651
+ * @param withAttributes - The boolean value to set
652
+ * @returns The QueryBuilder instance
653
+ *
654
+ * @example
655
+ * const builder = client.buildQuery()
656
+ * builder.withAttributes(true)
657
+ */
658
+ withAttributes(withAttributes = true) {
659
+ this._withAttributes = withAttributes;
660
+ return this;
661
+ }
662
+ /**
663
+ * Sets the withMetadata flag which will return the metadata (like owner, expiredAt, etc.) for the entities if true
664
+ * @param withMetadata - The boolean value to set
665
+ * @returns The QueryBuilder instance
666
+ *
667
+ * @example
668
+ * const builder = client.buildQuery()
669
+ * builder.withMetadata(true)
670
+ */
671
+ withMetadata(withMetadata = true) {
672
+ this._withMetadata = withMetadata;
673
+ return this;
674
+ }
675
+ /**
676
+ * Sets the withPayload flag which will return the payload for the entities if true
677
+ * @param withPayload - The boolean value to set
678
+ * @returns The QueryBuilder instance
679
+ *
680
+ * @example
681
+ * const builder = client.buildQuery()
682
+ * builder.withPayload(true)
683
+ */
684
+ withPayload(withPayload = true) {
685
+ this._withPayload = withPayload;
686
+ return this;
687
+ }
688
+ selectionParams() {
689
+ return {
690
+ withAttributes: this._withAttributes,
691
+ withMetadata: this._withMetadata,
692
+ withPayload: this._withPayload
693
+ };
694
+ }
695
+ projectEntity(rpcEntity) {
696
+ return entityFromRpcResult(rpcEntity);
697
+ }
698
+ };
699
+ /**
700
+ * SelectQueryBuilder is the recommended query builder. It requires the selection to be declared
701
+ * up front, so results always contain exactly the data you asked for — and the type of each
702
+ * returned entity is narrowed to exactly the selected fields.
703
+ *
704
+ * The selection is fixed at construction time and is flat — each field maps to an entity field.
705
+ * Create one via `client.select(...)` rather than constructing it directly, so the result type is
706
+ * inferred from the selection.
707
+ *
708
+ * @typeParam TEntity - The projected entity shape, inferred from the selection by `client.select()`.
709
+ *
710
+ * @example
711
+ * // everything
712
+ * await client.select().where(eq("name", "John")).fetch()
713
+ * await client.select("*").where(eq("name", "John")).fetch()
714
+ * // specific fields
715
+ * await client.select({ owner: true, attributes: true }).fetch()
716
+ * // a single field — result is typed (flat) { owner: Hex }
717
+ * await client.select({ owner: true }).fetch()
718
+ */
719
+ var SelectQueryBuilder = class extends BaseQueryBuilder {
720
+ _includeData;
721
+ constructor(client, selection) {
722
+ super(client);
723
+ this._includeData = selectionToIncludeData(selection);
724
+ }
725
+ selectionParams() {
726
+ return { includeData: this._includeData };
727
+ }
728
+ async projectEntity(rpcEntity) {
729
+ return await entityFromRpcResult(rpcEntity);
730
+ }
731
+ };
581
732
 
582
733
  //#endregion
583
- export { entityFromRpcResult as a, NoEntityFoundError as c, QueryResult as i, NoMoreResultsError as l, asc as n, EntityMutationError as o, desc as r, NoCursorOrLimitError as s, QueryBuilder as t, getLogger as u };
584
- //# sourceMappingURL=queryBuilder-DonAg1aq.js.map
734
+ export { getLogger as S, InvalidContentTypeError as _, desc as a, NoEntityFoundError as b, isEntityKey as c, entityFromRpcResult as d, RpcAttributeValueType as f, InvalidAttributeKeyError as g, InvalidAttributeError as h, asc as i, validateAttribute as l, EntityMutationError as m, QueryBuilder as n, selectionToIncludeData as o, DuplicateAttributeError as p, SelectQueryBuilder as r, QueryResult as s, BaseQueryBuilder as t, validateExpiresIn as u, InvalidExpirationError as v, NoMoreResultsError as x, NoCursorOrLimitError as y };
735
+ //# sourceMappingURL=queryBuilder-BE_XhLLa.js.map