@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.
- package/README.md +52 -16
- package/dist/consts-Bz0LZQZY.js +7 -0
- package/dist/consts-Bz0LZQZY.js.map +1 -0
- package/dist/consts-CxT1-RXU.cjs +18 -0
- package/dist/entity-BhSXaB23.cjs +81 -0
- package/dist/{query-CO6f35AE.d.cts → entity-CN41IOrq.d.cts} +125 -59
- package/dist/entity-CN41IOrq.d.cts.map +1 -0
- package/dist/entity-DGfVyB-a.js +71 -0
- package/dist/entity-DGfVyB-a.js.map +1 -0
- package/dist/{query-BhdmAzYg.d.ts → entity-NnEVlePN.d.ts} +125 -59
- package/dist/entity-NnEVlePN.d.ts.map +1 -0
- package/dist/index-ClkpS6s_.d.ts +481 -0
- package/dist/index-ClkpS6s_.d.ts.map +1 -0
- package/dist/index-w4qop0Pe.d.cts +481 -0
- package/dist/index-w4qop0Pe.d.cts.map +1 -0
- package/dist/index.cjs +285 -233
- package/dist/index.d.cts +100 -6
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +101 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +280 -230
- package/dist/index.js.map +1 -1
- package/dist/payload-BpNUPOnY.js +13 -0
- package/dist/payload-BpNUPOnY.js.map +1 -0
- package/dist/{payload-Da9gJMG5.d.ts → payload-C2KHOUoj.d.ts} +1 -1
- package/dist/{payload-Da9gJMG5.d.ts.map → payload-C2KHOUoj.d.ts.map} +1 -1
- package/dist/{payload-vwU3Rc6P.cjs → payload-D8YLKcJj.cjs} +1 -18
- package/dist/query/index.cjs +11 -28
- package/dist/query/index.d.cts +3 -3
- package/dist/query/index.d.ts +3 -3
- package/dist/query/index.js +8 -28
- package/dist/query/index.js.map +1 -1
- package/dist/{queryBuilder-DonAg1aq.js → queryBuilder-BE_XhLLa.js} +303 -152
- package/dist/queryBuilder-BE_XhLLa.js.map +1 -0
- package/dist/{queryBuilder-DM-YO1Y7.cjs → queryBuilder-J37buGCi.cjs} +372 -149
- package/dist/types/index.cjs +4 -1
- package/dist/types/index.d.cts +2 -2
- package/dist/types/index.d.ts +2 -2
- package/dist/types/index.js +4 -1
- package/dist/utils/index.cjs +4 -10
- package/dist/utils/index.d.cts +0 -1
- package/dist/utils/index.d.cts.map +1 -1
- package/dist/utils/index.d.ts +1 -2
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +2 -3
- package/dist/utils/index.js.map +1 -1
- package/package.json +11 -15
- package/src/actions/public/buildQuery.ts +4 -0
- package/src/actions/public/query.ts +10 -0
- package/src/actions/public/subscribeEntityEvents.ts +25 -51
- package/src/actions/wallet/changeOwnership.ts +6 -6
- package/src/actions/wallet/createEntity.ts +19 -6
- package/src/actions/wallet/deleteEntity.ts +2 -3
- package/src/actions/wallet/extendEntity.ts +9 -5
- package/src/actions/wallet/mutateEntities.ts +11 -54
- package/src/actions/wallet/updateEntity.ts +15 -6
- package/src/clients/createPublicClient.test.ts +2 -0
- package/src/clients/decorators/arkivPublic.ts +67 -2
- package/src/clients/decorators/arkivWallet.ts +23 -1
- package/src/consts.ts +2 -1
- package/src/errors.ts +46 -0
- package/src/index.ts +0 -3
- package/src/query/engine.test.ts +45 -0
- package/src/query/engine.ts +28 -14
- package/src/query/index.ts +1 -0
- package/src/query/predicate.test.ts +43 -0
- package/src/query/predicate.ts +14 -10
- package/src/query/queryBuilder.test.ts +22 -0
- package/src/query/queryBuilder.ts +221 -106
- package/src/query/queryResult.ts +13 -7
- package/src/query/selectQueryBuilder.test.ts +213 -0
- package/src/query/selection.test.ts +120 -0
- package/src/query/selection.ts +116 -0
- package/src/types/attributes.ts +25 -1
- package/src/types/entity.ts +12 -2
- package/src/types/events.ts +14 -16
- package/src/types/index.ts +1 -0
- package/src/types/rpcSchema.ts +7 -2
- package/src/utils/arkivTransactions.test.ts +260 -0
- package/src/utils/arkivTransactions.ts +255 -92
- package/src/utils/entities.ts +17 -7
- package/src/utils/index.ts +0 -1
- package/src/utils/payload.ts +4 -2
- package/src/utils/validation.test.ts +106 -0
- package/src/utils/validation.ts +80 -0
- package/dist/accounts/index.cjs +0 -9
- package/dist/accounts/index.d.cts +0 -1
- package/dist/accounts/index.d.ts +0 -1
- package/dist/accounts/index.js +0 -1
- package/dist/payload-DKBYYjeA.js +0 -18
- package/dist/payload-DKBYYjeA.js.map +0 -1
- package/dist/query-BhdmAzYg.d.ts.map +0 -1
- package/dist/query-CO6f35AE.d.cts.map +0 -1
- package/dist/queryBuilder-BvoQXa_u.d.ts +0 -328
- package/dist/queryBuilder-BvoQXa_u.d.ts.map +0 -1
- package/dist/queryBuilder-DonAg1aq.js.map +0 -1
- package/dist/queryBuilder-zTsKXctT.d.cts +0 -328
- package/dist/queryBuilder-zTsKXctT.d.cts.map +0 -1
- package/src/accounts/index.ts +0 -1
- /package/dist/{types-H-TSmRpm.js → types-Ci4BRClw.js} +0 -0
- /package/dist/{types-o3zWarRp.cjs → types-a8gm_IaQ.cjs} +0 -0
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import {
|
|
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
|
-
|
|
143
|
-
|
|
144
|
-
|
|
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.
|
|
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:
|
|
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
|
-
*
|
|
364
|
-
*
|
|
365
|
-
*
|
|
366
|
-
*
|
|
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
|
|
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
|
|
494
|
+
* @returns The query builder instance
|
|
388
495
|
*
|
|
389
496
|
* @example
|
|
390
|
-
* const builder =
|
|
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
|
|
507
|
+
* @returns The query builder instance
|
|
401
508
|
*
|
|
402
509
|
* @example
|
|
403
|
-
* const builder =
|
|
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
|
|
539
|
+
* @returns The query builder instance
|
|
472
540
|
*
|
|
473
541
|
* @example
|
|
474
|
-
* const builder =
|
|
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
|
|
552
|
+
* @returns The query builder instance
|
|
485
553
|
*
|
|
486
554
|
* @example
|
|
487
|
-
* const builder =
|
|
488
|
-
* builder.
|
|
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
|
|
566
|
+
* @returns The query builder instance
|
|
499
567
|
*
|
|
500
568
|
* @example
|
|
501
|
-
* const builder =
|
|
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
|
-
|
|
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.
|
|
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 =
|
|
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
|
-
|
|
549
|
-
withMetadata: this._withMetadata,
|
|
550
|
-
withPayload: this._withPayload
|
|
599
|
+
...this.selectionParams()
|
|
551
600
|
});
|
|
552
|
-
const entities = await Promise.all(queryResult.data.map((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 =
|
|
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 {
|
|
584
|
-
//# sourceMappingURL=queryBuilder-
|
|
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
|