@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.
- package/README.md +180 -61
- package/dist/chains/index.d.ts +17 -17
- package/dist/changeOwnership-Cy9gfyjN.d.cts +1160 -0
- package/dist/changeOwnership-Cy9gfyjN.d.cts.map +1 -0
- package/dist/changeOwnership-gXf-lBqI.d.ts +1160 -0
- package/dist/changeOwnership-gXf-lBqI.d.ts.map +1 -0
- package/dist/entity/index.cjs +4 -10
- package/dist/entity/index.d.cts +3 -3
- package/dist/entity/index.d.ts +3 -3
- package/dist/entity/index.js +3 -4
- package/dist/entity-BM1unrQf.cjs +41 -0
- package/dist/{entity-DJ0Gml9c.cjs → entity-BgaFJSaV.cjs} +4 -4
- package/dist/entity-CFAI1KyG.js +36 -0
- package/dist/entity-CFAI1KyG.js.map +1 -0
- package/dist/{entity-CniBgBSd.js → entity-CGqx_AU0.js} +4 -4
- package/dist/entity-CGqx_AU0.js.map +1 -0
- package/dist/{payload-AfUfSctb.d.ts → expirationTime-C7PZK49K.d.ts} +8 -37
- package/dist/expirationTime-C7PZK49K.d.ts.map +1 -0
- package/dist/expirationTime-CkIGNAg8.cjs +140 -0
- package/dist/{payload-D167BlJR.d.cts → expirationTime-DOB8V4B8.d.cts} +8 -37
- package/dist/expirationTime-DOB8V4B8.d.cts.map +1 -0
- package/dist/{expiry-Cgdb_dbq.js → expirationTime-UKPuA-LM.js} +62 -27
- package/dist/expirationTime-UKPuA-LM.js.map +1 -0
- package/dist/expiry-B48I-yTA.d.cts +40 -0
- package/dist/expiry-B48I-yTA.d.cts.map +1 -0
- package/dist/expiry-S2PUL8Hg.d.ts +40 -0
- package/dist/expiry-S2PUL8Hg.d.ts.map +1 -0
- package/dist/index-C-mMF5WZ.d.cts +735 -0
- package/dist/index-C-mMF5WZ.d.cts.map +1 -0
- package/dist/{index-BkqSnGJH.d.ts → index-C4zZqJ9S.d.ts} +5 -27
- package/dist/index-C4zZqJ9S.d.ts.map +1 -0
- package/dist/{index-CLhsFyVH.d.cts → index-CcVWYB6v.d.cts} +5 -27
- package/dist/index-CcVWYB6v.d.cts.map +1 -0
- package/dist/index-DCx2XC6S.d.ts +489 -0
- package/dist/index-DCx2XC6S.d.ts.map +1 -0
- package/dist/index-DLb7BfIw.d.ts +735 -0
- package/dist/index-DLb7BfIw.d.ts.map +1 -0
- package/dist/index-DzriOUm8.d.cts +489 -0
- package/dist/index-DzriOUm8.d.cts.map +1 -0
- package/dist/index.cjs +162 -146
- package/dist/index.d.cts +180 -133
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +180 -133
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +93 -83
- package/dist/index.js.map +1 -1
- package/dist/params-BuIrJA9a.cjs +45 -0
- package/dist/params-DDJOnsNi.js +22 -0
- package/dist/params-DDJOnsNi.js.map +1 -0
- package/dist/query/index.cjs +9 -4
- package/dist/query/index.d.cts +6 -6
- package/dist/query/index.d.ts +6 -6
- package/dist/query/index.js +4 -5
- package/dist/query-D0KtuFh2.cjs +1625 -0
- package/dist/query-DpRDozmj.js +1417 -0
- package/dist/query-DpRDozmj.js.map +1 -0
- package/dist/schema/index.cjs +46 -0
- package/dist/schema/index.d.cts +2 -0
- package/dist/schema/index.d.ts +2 -0
- package/dist/schema/index.js +3 -0
- package/dist/{attr-BXTkpnoc.cjs → schema-B9AyXcgU.cjs} +513 -246
- package/dist/{attr-tmyREHtw.js → schema-DY4sqehl.js} +427 -244
- package/dist/schema-DY4sqehl.js.map +1 -0
- package/dist/types/index.cjs +1 -1
- package/dist/types/index.d.cts +5 -5
- package/dist/types/index.d.ts +5 -5
- package/dist/types/index.js +1 -1
- package/dist/utils/index.cjs +3 -5
- package/dist/utils/index.d.cts +3 -3
- package/dist/utils/index.d.ts +3 -3
- package/dist/utils/index.js +3 -3
- package/package.json +9 -12
- package/src/actions/public/getEntity.test.ts +69 -0
- package/src/actions/public/getEntity.ts +34 -20
- package/src/actions/public/predictEntityKeys.ts +8 -7
- package/src/actions/public/query.test.ts +58 -0
- package/src/actions/public/query.ts +41 -13
- package/src/actions/wallet/createEntity.ts +34 -63
- package/src/actions/wallet/executeBatch.test.ts +38 -0
- package/src/actions/wallet/executeBatch.ts +26 -9
- package/src/actions/wallet/extendEntity.ts +9 -8
- package/src/actions/wallet/patchEntity.ts +43 -52
- package/src/clients/createPublicClient.test.ts +10 -2
- package/src/clients/createWalletClient.ts +4 -1
- package/src/clients/decorators/arkivPublic.ts +123 -80
- package/src/clients/decorators/arkivWallet.ts +113 -65
- package/src/entity/create.test.ts +25 -21
- package/src/entity/expiry.ts +8 -6
- package/src/entity/flags.ts +1 -1
- package/src/entity/index.ts +2 -4
- package/src/entity/key.ts +3 -3
- package/src/entity/operations.ts +1 -1
- package/src/errors.ts +2 -11
- package/src/index.ts +18 -6
- package/src/query/builderBase.ts +166 -0
- package/src/query/countBuilder.test.ts +91 -0
- package/src/query/countBuilder.ts +67 -0
- package/src/query/engine.ts +9 -3
- package/src/query/errors.ts +24 -14
- package/src/query/expression.test.ts +129 -21
- package/src/query/expression.ts +323 -94
- package/src/query/index.ts +26 -4
- package/src/query/injection.test.ts +5 -11
- package/src/query/queryBuilder.test.ts +467 -58
- package/src/query/queryBuilder.ts +191 -98
- package/src/query/queryResult.ts +35 -9
- package/src/query/selection.test.ts +278 -24
- package/src/query/selection.ts +427 -86
- package/src/schema/attributes.test.ts +266 -0
- package/src/schema/attributes.ts +212 -0
- package/src/{attr → schema}/codec.test.ts +37 -5
- package/src/{attr → schema}/codec.ts +40 -11
- package/src/schema/column.ts +193 -0
- package/src/{attr → schema}/errors.ts +67 -10
- package/src/schema/index.ts +76 -0
- package/src/{attr → schema}/names.ts +4 -2
- package/src/schema/schema.test.ts +171 -0
- package/src/schema/schema.ts +250 -0
- package/src/{attr → schema}/types.ts +36 -30
- package/src/{attr → schema}/values.test.ts +62 -3
- package/src/{attr → schema}/values.ts +246 -65
- package/src/types/entity.test.ts +1 -44
- package/src/types/entity.ts +9 -55
- package/src/types/events.ts +1 -1
- package/src/types/index.ts +11 -4
- package/src/types/rpcSchema.ts +22 -16
- package/src/utils/arkivTransactions.test.ts +356 -116
- package/src/utils/arkivTransactions.ts +30 -38
- package/src/utils/entities.ts +10 -9
- package/src/utils/expirationTime.ts +5 -5
- package/src/utils/index.ts +0 -1
- package/src/utils/logger.test.ts +45 -0
- package/src/utils/logger.ts +5 -39
- package/src/utils/revert.test.ts +25 -0
- package/src/utils/revert.ts +28 -3
- package/dist/attr/index.cjs +0 -34
- package/dist/attr/index.d.cts +0 -2
- package/dist/attr/index.d.ts +0 -2
- package/dist/attr/index.js +0 -3
- package/dist/attr-tmyREHtw.js.map +0 -1
- package/dist/entity-B0t6sN7F.cjs +0 -71
- package/dist/entity-BIp_df9u.js +0 -67
- package/dist/entity-BIp_df9u.js.map +0 -1
- package/dist/entity-CniBgBSd.js.map +0 -1
- package/dist/expiry-BxOlvVWC.d.ts +0 -79
- package/dist/expiry-BxOlvVWC.d.ts.map +0 -1
- package/dist/expiry-CG9GhVXv.cjs +0 -135
- package/dist/expiry-Cgdb_dbq.js.map +0 -1
- package/dist/expiry-DRV29F1r.d.cts +0 -79
- package/dist/expiry-DRV29F1r.d.cts.map +0 -1
- package/dist/flags-DCD26uhe.cjs +0 -43
- package/dist/flags-DUT2gHEU.js +0 -33
- package/dist/flags-DUT2gHEU.js.map +0 -1
- package/dist/index-BkqSnGJH.d.ts.map +0 -1
- package/dist/index-C7fbakoI.d.cts +0 -278
- package/dist/index-C7fbakoI.d.cts.map +0 -1
- package/dist/index-CIcHPx2j.d.cts +0 -424
- package/dist/index-CIcHPx2j.d.cts.map +0 -1
- package/dist/index-CLhsFyVH.d.cts.map +0 -1
- package/dist/index-Cd2aaWme.d.ts +0 -424
- package/dist/index-Cd2aaWme.d.ts.map +0 -1
- package/dist/index-Cq6fa8AI.d.ts +0 -278
- package/dist/index-Cq6fa8AI.d.ts.map +0 -1
- package/dist/index-DaNeJUw8.d.ts +0 -955
- package/dist/index-DaNeJUw8.d.ts.map +0 -1
- package/dist/index-Dv16LO2s.d.cts +0 -955
- package/dist/index-Dv16LO2s.d.cts.map +0 -1
- package/dist/payload-AfUfSctb.d.ts.map +0 -1
- package/dist/payload-D167BlJR.d.cts.map +0 -1
- package/dist/payload-DE7wMztL.js +0 -92
- package/dist/payload-DE7wMztL.js.map +0 -1
- package/dist/payload-DqFweqiO.cjs +0 -108
- package/dist/query-9oojUdM7.cjs +0 -1128
- package/dist/query-Ber-lOJQ.js +0 -968
- package/dist/query-Ber-lOJQ.js.map +0 -1
- package/src/actions/public/getEntityCount.ts +0 -10
- package/src/attr/attributes.test.ts +0 -120
- package/src/attr/attributes.ts +0 -196
- package/src/attr/index.ts +0 -50
- package/src/types/mimeTypes.ts +0 -27
- package/src/utils/compression.test.ts +0 -32
- package/src/utils/compression.ts +0 -102
- package/src/utils/payload.ts +0 -36
- /package/src/{attr → schema}/decimal.ts +0 -0
|
@@ -11,22 +11,29 @@ import {
|
|
|
11
11
|
parseAbiParameters,
|
|
12
12
|
toHex,
|
|
13
13
|
} from "viem"
|
|
14
|
+
import type { ArkivClient } from "../clients/baseClient"
|
|
15
|
+
import { ARKIV_ADDRESS } from "../consts"
|
|
16
|
+
import { InvalidExpiryError, NO_SALT } from "../entity"
|
|
17
|
+
import { ENTITY_EVENTS_ABI, type EntityEventName } from "../entity/events"
|
|
18
|
+
import { MAX_EXPIRES_AT, OperationType } from "../entity/params"
|
|
19
|
+
import { EmptyPatchError, EntityMutationError } from "../errors"
|
|
14
20
|
import {
|
|
15
|
-
|
|
21
|
+
addr,
|
|
22
|
+
bytes,
|
|
16
23
|
ConflictingMutationError,
|
|
17
24
|
dec,
|
|
25
|
+
defineSchema,
|
|
26
|
+
EntityTooLargeError,
|
|
18
27
|
InvalidAttributeNameError,
|
|
19
28
|
InvalidValueError,
|
|
20
29
|
i32,
|
|
30
|
+
MAX_BYTES_LENGTH,
|
|
31
|
+
MAX_ENTITY_BYTES,
|
|
21
32
|
MissingValueError,
|
|
33
|
+
SchemaMismatchError,
|
|
34
|
+
str,
|
|
22
35
|
u256,
|
|
23
|
-
} from "../
|
|
24
|
-
import type { ArkivClient } from "../clients/baseClient"
|
|
25
|
-
import { ARKIV_ADDRESS } from "../consts"
|
|
26
|
-
import { InvalidExpiryError, NO_SALT } from "../entity"
|
|
27
|
-
import { ENTITY_EVENTS_ABI, type EntityEventName } from "../entity/events"
|
|
28
|
-
import { MAX_EXPIRES_AT, OperationType } from "../entity/params"
|
|
29
|
-
import { EmptyPatchError, EntityMutationError } from "../errors"
|
|
36
|
+
} from "../schema"
|
|
30
37
|
import { sendArkivTransaction } from "./arkivTransactions"
|
|
31
38
|
import { ExpirationTime } from "./expirationTime"
|
|
32
39
|
|
|
@@ -151,13 +158,22 @@ function sentExtension(writeContract: ReturnType<typeof vi.fn>) {
|
|
|
151
158
|
return decodeAbiParameters(EXTEND_PARAMS, op.operationData)[0]
|
|
152
159
|
}
|
|
153
160
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
161
|
+
/** The typeId bit that asks the node to index an attribute. */
|
|
162
|
+
const INDEXED = 0x80
|
|
163
|
+
|
|
164
|
+
const Docs = defineSchema({
|
|
165
|
+
category: str().indexed(),
|
|
166
|
+
level: i32().indexed().optional(),
|
|
167
|
+
title: str().optional(),
|
|
168
|
+
})
|
|
169
|
+
|
|
170
|
+
/** A minimal valid create's schema and values, without its expiry. */
|
|
171
|
+
const CONTENTS = { schema: Docs, values: { category: "docs" } }
|
|
158
172
|
|
|
159
173
|
const DAY = ExpirationTime.fromDays(1) // 86400s -> 43200 blocks at 2s
|
|
160
|
-
const validCreate = { ...CONTENTS,
|
|
174
|
+
const validCreate = { ...CONTENTS, expiration: DAY }
|
|
175
|
+
|
|
176
|
+
const name = (n: string) => toHex(n, { size: 32 })
|
|
161
177
|
|
|
162
178
|
describe("create expiry", () => {
|
|
163
179
|
it("sends a create for a valid expiry", async () => {
|
|
@@ -173,7 +189,7 @@ describe("create expiry", () => {
|
|
|
173
189
|
creates: [
|
|
174
190
|
{
|
|
175
191
|
...CONTENTS,
|
|
176
|
-
|
|
192
|
+
expiration: ExpirationTime.atBlock(5_000n, { atLeast: ExpirationTime.fromSeconds(200) }),
|
|
177
193
|
},
|
|
178
194
|
],
|
|
179
195
|
})
|
|
@@ -185,19 +201,21 @@ describe("create expiry", () => {
|
|
|
185
201
|
it("reports the block the entity will actually expire at", async () => {
|
|
186
202
|
const { client } = makeClient()
|
|
187
203
|
const relative = await sendArkivTransaction(client, {
|
|
188
|
-
creates: [{ ...CONTENTS,
|
|
204
|
+
creates: [{ ...CONTENTS, expiration: DAY }],
|
|
189
205
|
})
|
|
190
206
|
expect(relative.createdExpiries[0]).toBe(CURRENT_BLOCK + 43_200n)
|
|
191
207
|
|
|
192
208
|
const { client: client2 } = makeClient()
|
|
193
209
|
const absolute = await sendArkivTransaction(client2, {
|
|
194
|
-
creates: [{ ...CONTENTS,
|
|
210
|
+
creates: [{ ...CONTENTS, expiration: ExpirationTime.atBlock(5_000n) }],
|
|
195
211
|
})
|
|
196
212
|
expect(absolute.createdExpiries[0]).toBe(5_000n)
|
|
197
213
|
|
|
198
214
|
const { client: client3 } = makeClient()
|
|
199
215
|
const dated = await sendArkivTransaction(client3, {
|
|
200
|
-
creates: [
|
|
216
|
+
creates: [
|
|
217
|
+
{ ...CONTENTS, expiration: ExpirationTime.atDate(new Date(Date.now() + 3600_000)) },
|
|
218
|
+
],
|
|
201
219
|
})
|
|
202
220
|
// An hour at 2s/block, counted from the block the transaction was built on.
|
|
203
221
|
expect(dated.createdExpiries[0]).toBe(CURRENT_BLOCK + 1800n)
|
|
@@ -214,7 +232,7 @@ describe("create expiry", () => {
|
|
|
214
232
|
const { client, writeContract } = makeClient()
|
|
215
233
|
await expect(
|
|
216
234
|
sendArkivTransaction(client, {
|
|
217
|
-
creates: [{ ...CONTENTS,
|
|
235
|
+
creates: [{ ...CONTENTS, expiration: ExpirationTime.atBlock(CURRENT_BLOCK - 1n) }],
|
|
218
236
|
}),
|
|
219
237
|
).rejects.toThrow(/dead on arrival/)
|
|
220
238
|
expect(writeContract).not.toHaveBeenCalled()
|
|
@@ -233,7 +251,7 @@ describe("extend expiry", () => {
|
|
|
233
251
|
it("resolves an extension exactly like a create, both forms", async () => {
|
|
234
252
|
const { client, writeContract } = makeClient()
|
|
235
253
|
const relative = await sendArkivTransaction(client, {
|
|
236
|
-
extensions: [{ entityKey: ENTITY_KEY,
|
|
254
|
+
extensions: [{ entityKey: ENTITY_KEY, expiration: DAY }],
|
|
237
255
|
})
|
|
238
256
|
const extension = sentExtension(writeContract)
|
|
239
257
|
expect(extension.entityKey).toBe(ENTITY_KEY)
|
|
@@ -244,7 +262,7 @@ describe("extend expiry", () => {
|
|
|
244
262
|
|
|
245
263
|
const { client: client2, writeContract: writeContract2 } = makeClient()
|
|
246
264
|
const absolute = await sendArkivTransaction(client2, {
|
|
247
|
-
extensions: [{ entityKey: ENTITY_KEY,
|
|
265
|
+
extensions: [{ entityKey: ENTITY_KEY, expiration: ExpirationTime.atBlock(5_000n) }],
|
|
248
266
|
})
|
|
249
267
|
expect(sentExtension(writeContract2).expiresAt).toBe(5_000n)
|
|
250
268
|
expect(absolute.extendedExpiries[0]).toBe(5_000n)
|
|
@@ -254,7 +272,7 @@ describe("extend expiry", () => {
|
|
|
254
272
|
const { client, writeContract } = makeClient()
|
|
255
273
|
const tooFar = ExpirationTime.atBlock(MAX_EXPIRES_AT + 1n)
|
|
256
274
|
await expect(
|
|
257
|
-
sendArkivTransaction(client, { extensions: [{ entityKey: ENTITY_KEY,
|
|
275
|
+
sendArkivTransaction(client, { extensions: [{ entityKey: ENTITY_KEY, expiration: tooFar }] }),
|
|
258
276
|
).rejects.toThrow(/does not fit the uint64/)
|
|
259
277
|
expect(writeContract).not.toHaveBeenCalled()
|
|
260
278
|
})
|
|
@@ -270,7 +288,10 @@ describe("extend expiry", () => {
|
|
|
270
288
|
const { client, writeContract } = makeClient()
|
|
271
289
|
await sendArkivTransaction(client, {
|
|
272
290
|
extensions: [
|
|
273
|
-
{
|
|
291
|
+
{
|
|
292
|
+
entityKey: ENTITY_KEY,
|
|
293
|
+
expiration: ExpirationTime.atDate(new Date(Date.now() + 3600_000)),
|
|
294
|
+
},
|
|
274
295
|
],
|
|
275
296
|
})
|
|
276
297
|
// A Date can only be placed once the current block is known, so it must have been fetched even
|
|
@@ -401,107 +422,248 @@ describe("entity keys", () => {
|
|
|
401
422
|
})
|
|
402
423
|
})
|
|
403
424
|
|
|
404
|
-
describe("
|
|
425
|
+
describe("create through a schema", () => {
|
|
426
|
+
/** The create's attributes, keyed by their bytes32 name. */
|
|
427
|
+
function sentCells(writeContract: ReturnType<typeof vi.fn>) {
|
|
428
|
+
return Object.fromEntries(sentCreate(writeContract).attributes.map((a) => [a.name, a]))
|
|
429
|
+
}
|
|
430
|
+
|
|
405
431
|
it("rejects a value that does not fit the type it defaults to", async () => {
|
|
406
432
|
const { client } = makeClient()
|
|
433
|
+
const schema = defineSchema({ n: i32() })
|
|
407
434
|
await expect(
|
|
408
|
-
sendArkivTransaction(client, { creates: [{
|
|
435
|
+
sendArkivTransaction(client, { creates: [{ schema, values: { n: 1.5 }, expiration: DAY }] }),
|
|
409
436
|
).rejects.toThrow(InvalidValueError)
|
|
410
437
|
})
|
|
411
438
|
|
|
412
|
-
it("rejects
|
|
439
|
+
it("rejects a column name outside the grammar before anything is written", () => {
|
|
440
|
+
expect(() => defineSchema({ "1bad": i32() })).toThrow(InvalidAttributeNameError)
|
|
441
|
+
})
|
|
442
|
+
|
|
443
|
+
it("rejects a null value, rather than writing a typeless one", async () => {
|
|
444
|
+
const { client, writeContract } = makeClient()
|
|
445
|
+
await expect(
|
|
446
|
+
sendArkivTransaction(client, {
|
|
447
|
+
creates: [{ ...validCreate, values: { category: "docs", title: null as never } }],
|
|
448
|
+
}),
|
|
449
|
+
).rejects.toThrow(MissingValueError)
|
|
450
|
+
expect(writeContract).not.toHaveBeenCalled()
|
|
451
|
+
})
|
|
452
|
+
|
|
453
|
+
it("rejects a create that leaves out a required column", async () => {
|
|
454
|
+
const { client, writeContract } = makeClient()
|
|
455
|
+
await expect(
|
|
456
|
+
sendArkivTransaction(client, { creates: [{ ...validCreate, values: {} }] }),
|
|
457
|
+
).rejects.toThrow(SchemaMismatchError)
|
|
458
|
+
// Explicitly undefined is the same as left out.
|
|
459
|
+
await expect(
|
|
460
|
+
sendArkivTransaction(client, {
|
|
461
|
+
creates: [{ ...validCreate, values: { category: undefined } as never }],
|
|
462
|
+
}),
|
|
463
|
+
).rejects.toThrow(/required str column/)
|
|
464
|
+
expect(writeContract).not.toHaveBeenCalled()
|
|
465
|
+
})
|
|
466
|
+
|
|
467
|
+
it("checks the schema before any network call, so a transport failure cannot mask it", async () => {
|
|
413
468
|
const { client } = makeClient()
|
|
469
|
+
const getBlockNumber = (client as unknown as { getBlockNumber: ReturnType<typeof vi.fn> })
|
|
470
|
+
.getBlockNumber
|
|
471
|
+
getBlockNumber.mockRejectedValue(new Error("fetch failed"))
|
|
414
472
|
await expect(
|
|
415
|
-
sendArkivTransaction(client, { creates: [{ ...validCreate,
|
|
416
|
-
).rejects.toThrow(
|
|
473
|
+
sendArkivTransaction(client, { creates: [{ ...validCreate, values: {} }] }),
|
|
474
|
+
).rejects.toThrow(SchemaMismatchError)
|
|
475
|
+
expect(getBlockNumber).not.toHaveBeenCalled()
|
|
417
476
|
})
|
|
418
477
|
|
|
419
|
-
it("rejects
|
|
478
|
+
it("rejects a column the schema does not have", async () => {
|
|
420
479
|
const { client } = makeClient()
|
|
480
|
+
const failure = await sendArkivTransaction(client, {
|
|
481
|
+
creates: [{ ...validCreate, values: { category: "docs", nope: 1 } }],
|
|
482
|
+
}).catch((error: Error) => error)
|
|
483
|
+
expect(failure).toBeInstanceOf(SchemaMismatchError)
|
|
484
|
+
expect((failure as SchemaMismatchError).columnName).toBe("nope")
|
|
485
|
+
})
|
|
486
|
+
|
|
487
|
+
it("rejects a value whose type is not its column's", async () => {
|
|
488
|
+
const { client } = makeClient()
|
|
489
|
+
await expect(
|
|
490
|
+
sendArkivTransaction(client, { creates: [{ ...validCreate, values: { category: i32(1) } }] }),
|
|
491
|
+
).rejects.toThrow(SchemaMismatchError)
|
|
492
|
+
// A bare string is a str, never coerced into the column's type.
|
|
493
|
+
const schema = defineSchema({ score: dec() })
|
|
421
494
|
await expect(
|
|
422
495
|
sendArkivTransaction(client, {
|
|
423
|
-
creates: [
|
|
424
|
-
{
|
|
425
|
-
...validCreate,
|
|
426
|
-
// What a spread of an optional-shaped object produces at runtime.
|
|
427
|
-
attributes: { category: "docs", score: undefined } as unknown as AttributeInputs,
|
|
428
|
-
},
|
|
429
|
-
],
|
|
496
|
+
creates: [{ schema, values: { score: "4.1" }, expiration: DAY }],
|
|
430
497
|
}),
|
|
431
|
-
).rejects.toThrow(
|
|
498
|
+
).rejects.toThrow(/a dec column, but the value is a str/)
|
|
432
499
|
})
|
|
433
500
|
|
|
434
|
-
it("
|
|
501
|
+
it("lets an optional column be left out entirely", async () => {
|
|
435
502
|
const { client, writeContract } = makeClient()
|
|
436
503
|
await sendArkivTransaction(client, { creates: [validCreate] })
|
|
437
|
-
expect(sentCreate(writeContract).attributes.map((a) => a.name)).toEqual([
|
|
438
|
-
toHex("$contentType", { size: 32 }),
|
|
439
|
-
toHex("$payload", { size: 32 }),
|
|
440
|
-
])
|
|
504
|
+
expect(sentCreate(writeContract).attributes.map((a) => a.name)).toEqual([name("category")])
|
|
441
505
|
})
|
|
442
506
|
|
|
443
|
-
it("writes
|
|
507
|
+
it("writes a defaulted column's default when the create leaves it out", async () => {
|
|
508
|
+
const schema = defineSchema({ category: str(), mimeType: str().default("text/markdown") })
|
|
444
509
|
const { client, writeContract } = makeClient()
|
|
445
510
|
await sendArkivTransaction(client, {
|
|
446
|
-
creates: [{
|
|
511
|
+
creates: [{ schema, values: { category: "docs" }, expiration: DAY }],
|
|
512
|
+
})
|
|
513
|
+
expect(sentCells(writeContract)[name("mimeType")]).toMatchObject({
|
|
514
|
+
typeId: 8,
|
|
515
|
+
value: toHex("text/markdown"),
|
|
516
|
+
})
|
|
517
|
+
|
|
518
|
+
// ...and the value given, when it is given.
|
|
519
|
+
const { client: c2, writeContract: w2 } = makeClient()
|
|
520
|
+
await sendArkivTransaction(c2, {
|
|
521
|
+
creates: [{ schema, values: { category: "docs", mimeType: "text/plain" }, expiration: DAY }],
|
|
447
522
|
})
|
|
448
|
-
|
|
449
|
-
expect(byName[toHex("$payload", { size: 32 })]).toMatchObject({ typeId: 7, value: "0x" })
|
|
523
|
+
expect(sentCells(w2)[name("mimeType")].value).toBe(toHex("text/plain"))
|
|
450
524
|
})
|
|
451
525
|
|
|
452
|
-
it("
|
|
526
|
+
it("flags indexed columns in bit 7 of the typeId, and leaves the rest unindexed", async () => {
|
|
527
|
+
const schema = defineSchema({
|
|
528
|
+
count: i32().indexed(),
|
|
529
|
+
balance: u256().indexed(),
|
|
530
|
+
score: dec().indexed(),
|
|
531
|
+
author: addr(),
|
|
532
|
+
title: str(),
|
|
533
|
+
})
|
|
453
534
|
const { client, writeContract } = makeClient()
|
|
454
535
|
await sendArkivTransaction(client, {
|
|
455
536
|
creates: [
|
|
456
|
-
{
|
|
537
|
+
{
|
|
538
|
+
schema,
|
|
539
|
+
values: {
|
|
540
|
+
count: 42,
|
|
541
|
+
balance: u256(1n),
|
|
542
|
+
score: dec("3.5"),
|
|
543
|
+
author: addr("0x1111111111111111111111111111111111111111"),
|
|
544
|
+
title: "Cells",
|
|
545
|
+
},
|
|
546
|
+
expiration: DAY,
|
|
547
|
+
},
|
|
457
548
|
],
|
|
458
549
|
})
|
|
459
|
-
const
|
|
550
|
+
const typeIds = Object.fromEntries(
|
|
460
551
|
sentCreate(writeContract).attributes.map((a) => [a.name, a.typeId]),
|
|
461
552
|
)
|
|
462
|
-
expect(
|
|
463
|
-
expect(
|
|
464
|
-
expect(
|
|
553
|
+
expect(typeIds[name("count")]).toBe(INDEXED | 2)
|
|
554
|
+
expect(typeIds[name("balance")]).toBe(INDEXED | 4)
|
|
555
|
+
expect(typeIds[name("score")]).toBe(INDEXED | 5)
|
|
556
|
+
expect(typeIds[name("author")]).toBe(9)
|
|
557
|
+
expect(typeIds[name("title")]).toBe(8)
|
|
465
558
|
})
|
|
466
559
|
|
|
467
|
-
it("
|
|
560
|
+
it("writes any number of bytes columns, unindexed, from bare Uint8Arrays or bytes()", async () => {
|
|
561
|
+
const schema = defineSchema({ body: bytes(), thumbnail: bytes(), empty: bytes() })
|
|
468
562
|
const { client, writeContract } = makeClient()
|
|
469
563
|
await sendArkivTransaction(client, {
|
|
470
564
|
creates: [
|
|
471
565
|
{
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
566
|
+
schema,
|
|
567
|
+
values: {
|
|
568
|
+
body: new Uint8Array([1, 2, 3]),
|
|
569
|
+
thumbnail: bytes("0xDEAD"),
|
|
570
|
+
empty: new Uint8Array(),
|
|
571
|
+
},
|
|
572
|
+
expiration: DAY,
|
|
476
573
|
},
|
|
477
574
|
],
|
|
478
575
|
})
|
|
479
|
-
const
|
|
480
|
-
expect(
|
|
481
|
-
expect(
|
|
576
|
+
const cells = sentCells(writeContract)
|
|
577
|
+
expect(cells[name("body")]).toMatchObject({ typeId: 7, value: "0x010203" })
|
|
578
|
+
expect(cells[name("thumbnail")]).toMatchObject({ typeId: 7, value: "0xdead" })
|
|
579
|
+
// Empty is a value, not an absence: it is written rather than dropped.
|
|
580
|
+
expect(cells[name("empty")]).toMatchObject({ typeId: 7, value: "0x" })
|
|
482
581
|
})
|
|
483
582
|
|
|
484
|
-
it("
|
|
485
|
-
const {
|
|
583
|
+
it("caps each bytes value at 128 KiB", async () => {
|
|
584
|
+
const schema = defineSchema({ body: bytes(), thumbnail: bytes().optional() })
|
|
585
|
+
const { client, writeContract } = makeClient()
|
|
586
|
+
await sendArkivTransaction(client, {
|
|
587
|
+
creates: [{ schema, values: { body: new Uint8Array(MAX_BYTES_LENGTH) }, expiration: DAY }],
|
|
588
|
+
})
|
|
589
|
+
expect(writeContract).toHaveBeenCalledTimes(1)
|
|
590
|
+
|
|
591
|
+
const { client: c2 } = makeClient()
|
|
486
592
|
await expect(
|
|
487
|
-
sendArkivTransaction(
|
|
488
|
-
|
|
593
|
+
sendArkivTransaction(c2, {
|
|
594
|
+
creates: [
|
|
595
|
+
{ schema, values: { body: new Uint8Array(MAX_BYTES_LENGTH + 1) }, expiration: DAY },
|
|
596
|
+
],
|
|
597
|
+
}),
|
|
598
|
+
).rejects.toThrow(InvalidValueError)
|
|
599
|
+
})
|
|
600
|
+
|
|
601
|
+
it("caps the bytes of one create at 128 KiB in total", async () => {
|
|
602
|
+
const schema = defineSchema({ body: bytes(), thumbnail: bytes() })
|
|
603
|
+
const half = new Uint8Array(MAX_ENTITY_BYTES / 2)
|
|
604
|
+
const { client, writeContract } = makeClient()
|
|
605
|
+
await sendArkivTransaction(client, {
|
|
606
|
+
creates: [{ schema, values: { body: half, thumbnail: half }, expiration: DAY }],
|
|
607
|
+
})
|
|
608
|
+
expect(writeContract).toHaveBeenCalledTimes(1)
|
|
609
|
+
|
|
610
|
+
const { client: c2, writeContract: w2 } = makeClient()
|
|
611
|
+
await expect(
|
|
612
|
+
sendArkivTransaction(c2, {
|
|
613
|
+
creates: [
|
|
614
|
+
{
|
|
615
|
+
schema,
|
|
616
|
+
values: { body: half, thumbnail: new Uint8Array(MAX_ENTITY_BYTES / 2 + 1) },
|
|
617
|
+
expiration: DAY,
|
|
618
|
+
},
|
|
619
|
+
],
|
|
620
|
+
}),
|
|
621
|
+
).rejects.toThrow(EntityTooLargeError)
|
|
622
|
+
expect(w2).not.toHaveBeenCalled()
|
|
623
|
+
})
|
|
624
|
+
|
|
625
|
+
it("carries no cells for a schema with no columns", async () => {
|
|
626
|
+
const { client, writeContract } = makeClient()
|
|
627
|
+
await sendArkivTransaction(client, {
|
|
628
|
+
creates: [{ schema: defineSchema({}), values: {}, expiration: DAY }],
|
|
629
|
+
})
|
|
630
|
+
expect(sentCreate(writeContract).attributes).toEqual([])
|
|
489
631
|
})
|
|
490
632
|
|
|
491
633
|
it("sorts attributes ascending by their bytes32 name", async () => {
|
|
634
|
+
const schema = defineSchema({ tag: str(), status: str().indexed(), score: i32() })
|
|
492
635
|
const { client, writeContract } = makeClient()
|
|
493
636
|
await sendArkivTransaction(client, {
|
|
494
|
-
creates: [{
|
|
495
|
-
})
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
toHex("score", { size: 32 }),
|
|
501
|
-
toHex("status", { size: 32 }),
|
|
502
|
-
toHex("tag", { size: 32 }),
|
|
637
|
+
creates: [{ schema, values: { tag: "zz", status: "active", score: 30 }, expiration: DAY }],
|
|
638
|
+
})
|
|
639
|
+
expect(sentCreate(writeContract).attributes.map((a) => a.name)).toEqual([
|
|
640
|
+
name("score"),
|
|
641
|
+
name("status"),
|
|
642
|
+
name("tag"),
|
|
503
643
|
])
|
|
504
644
|
})
|
|
645
|
+
|
|
646
|
+
it("writes each create of a batch through its own schema", async () => {
|
|
647
|
+
const Files = defineSchema({ path: str().indexed(), body: bytes() })
|
|
648
|
+
const { client, writeContract } = makeClient()
|
|
649
|
+
const result = await sendArkivTransaction(client, {
|
|
650
|
+
creates: [
|
|
651
|
+
validCreate,
|
|
652
|
+
{ schema: Files, values: { path: "/a", body: new Uint8Array([7]) }, expiration: DAY },
|
|
653
|
+
],
|
|
654
|
+
})
|
|
655
|
+
const [docs, files] = sentOperations(writeContract).map(
|
|
656
|
+
(op) => decodeAbiParameters(CREATE_PARAMS, op.operationData)[0],
|
|
657
|
+
)
|
|
658
|
+
expect(docs.attributes.map((a) => [a.name, a.typeId])).toEqual([
|
|
659
|
+
[name("category"), INDEXED | 8],
|
|
660
|
+
])
|
|
661
|
+
expect(files.attributes.map((a) => [a.name, a.typeId])).toEqual([
|
|
662
|
+
[name("body"), 7],
|
|
663
|
+
[name("path"), INDEXED | 8],
|
|
664
|
+
])
|
|
665
|
+
expect(result.createdEntityKeys).toEqual([mintedKey(0), mintedKey(1)])
|
|
666
|
+
})
|
|
505
667
|
})
|
|
506
668
|
|
|
507
669
|
describe("patch", () => {
|
|
@@ -512,64 +674,145 @@ describe("patch", () => {
|
|
|
512
674
|
return decodeAbiParameters(PATCH_PARAMS, op.operationData)[0]
|
|
513
675
|
}
|
|
514
676
|
|
|
515
|
-
it("carries only what the patch named", async () => {
|
|
677
|
+
it("carries only what the patch named, flagged as its column declares", async () => {
|
|
516
678
|
const { client, writeContract } = makeClient()
|
|
517
679
|
await sendArkivTransaction(client, {
|
|
518
|
-
patches: [{ entityKey: ENTITY_KEY, set: { level: i32(11) } }],
|
|
680
|
+
patches: [{ schema: Docs, entityKey: ENTITY_KEY, set: { level: i32(11) } }],
|
|
519
681
|
})
|
|
520
682
|
const patch = sentPatch(writeContract)
|
|
521
683
|
expect(patch.entityKey).toBe(ENTITY_KEY)
|
|
522
|
-
// No
|
|
684
|
+
// No tombstones, nothing unnamed: everything else is left alone.
|
|
523
685
|
expect(patch.mutations).toEqual([
|
|
524
|
-
{ name:
|
|
686
|
+
{ name: name("level"), typeId: INDEXED | 2, value: expect.any(String) },
|
|
687
|
+
])
|
|
688
|
+
})
|
|
689
|
+
|
|
690
|
+
it("flips a column's indexing when the schema writing it says so", async () => {
|
|
691
|
+
const Indexed = defineSchema({ title: str().indexed() })
|
|
692
|
+
const { client, writeContract } = makeClient()
|
|
693
|
+
await sendArkivTransaction(client, {
|
|
694
|
+
patches: [{ schema: Indexed, entityKey: ENTITY_KEY, set: { title: "x" } }],
|
|
695
|
+
})
|
|
696
|
+
expect(sentPatch(writeContract).mutations[0].typeId).toBe(INDEXED | 8)
|
|
697
|
+
|
|
698
|
+
const { client: c2, writeContract: w2 } = makeClient()
|
|
699
|
+
await sendArkivTransaction(c2, {
|
|
700
|
+
patches: [{ schema: Docs, entityKey: ENTITY_KEY, set: { title: "x" } }],
|
|
701
|
+
})
|
|
702
|
+
expect(sentPatch(w2).mutations[0].typeId).toBe(8)
|
|
703
|
+
})
|
|
704
|
+
|
|
705
|
+
it("does not apply defaults — a patch writes only what it names", async () => {
|
|
706
|
+
const schema = defineSchema({ category: str(), mimeType: str().default("text/markdown") })
|
|
707
|
+
const { client, writeContract } = makeClient()
|
|
708
|
+
await sendArkivTransaction(client, {
|
|
709
|
+
patches: [{ schema, entityKey: ENTITY_KEY, set: { category: "x" } }],
|
|
710
|
+
})
|
|
711
|
+
expect(sentPatch(writeContract).mutations.map((m) => m.name)).toEqual([name("category")])
|
|
712
|
+
})
|
|
713
|
+
|
|
714
|
+
it("tombstones an unset optional column with typeId 0 and an empty value", async () => {
|
|
715
|
+
const { client, writeContract } = makeClient()
|
|
716
|
+
await sendArkivTransaction(client, {
|
|
717
|
+
patches: [{ schema: Docs, entityKey: ENTITY_KEY, unset: ["title"] }],
|
|
718
|
+
})
|
|
719
|
+
expect(sentPatch(writeContract).mutations).toEqual([
|
|
720
|
+
{ name: name("title"), typeId: 0, value: "0x" },
|
|
525
721
|
])
|
|
526
722
|
})
|
|
527
723
|
|
|
528
|
-
it("
|
|
724
|
+
it("refuses to unset a required column", async () => {
|
|
725
|
+
const { client, writeContract } = makeClient()
|
|
726
|
+
await expect(
|
|
727
|
+
sendArkivTransaction(client, {
|
|
728
|
+
patches: [{ schema: Docs, entityKey: ENTITY_KEY, unset: ["category" as never] }],
|
|
729
|
+
}),
|
|
730
|
+
).rejects.toThrow(SchemaMismatchError)
|
|
731
|
+
expect(writeContract).not.toHaveBeenCalled()
|
|
732
|
+
})
|
|
733
|
+
|
|
734
|
+
it("rejects a set of a column the schema does not have, or of the wrong type", async () => {
|
|
735
|
+
const { client } = makeClient()
|
|
736
|
+
await expect(
|
|
737
|
+
sendArkivTransaction(client, {
|
|
738
|
+
patches: [{ schema: Docs, entityKey: ENTITY_KEY, set: { nope: 1 } as never }],
|
|
739
|
+
}),
|
|
740
|
+
).rejects.toThrow(SchemaMismatchError)
|
|
741
|
+
await expect(
|
|
742
|
+
sendArkivTransaction(client, {
|
|
743
|
+
patches: [{ schema: Docs, entityKey: ENTITY_KEY, set: { level: "high" } as never }],
|
|
744
|
+
}),
|
|
745
|
+
).rejects.toThrow(/i32 column, but the value is a str/)
|
|
746
|
+
})
|
|
747
|
+
|
|
748
|
+
it("leaves out a column set to undefined, as a create does", async () => {
|
|
529
749
|
const { client, writeContract } = makeClient()
|
|
530
750
|
await sendArkivTransaction(client, {
|
|
531
|
-
patches: [{ entityKey: ENTITY_KEY,
|
|
751
|
+
patches: [{ schema: Docs, entityKey: ENTITY_KEY, set: { level: undefined, title: "x" } }],
|
|
532
752
|
})
|
|
533
|
-
|
|
534
|
-
|
|
753
|
+
expect(sentPatch(writeContract).mutations.map((m) => m.name)).toEqual([name("title")])
|
|
754
|
+
})
|
|
755
|
+
|
|
756
|
+
it("treats a patch of nothing but undefined values as empty", async () => {
|
|
757
|
+
const { client } = makeClient()
|
|
758
|
+
await expect(
|
|
759
|
+
sendArkivTransaction(client, {
|
|
760
|
+
patches: [{ schema: Docs, entityKey: ENTITY_KEY, set: { level: undefined } }],
|
|
761
|
+
}),
|
|
762
|
+
).rejects.toThrow(EmptyPatchError)
|
|
535
763
|
})
|
|
536
764
|
|
|
537
|
-
it("
|
|
765
|
+
it("does not count an undefined set as conflicting with an unset", async () => {
|
|
538
766
|
const { client, writeContract } = makeClient()
|
|
539
767
|
await sendArkivTransaction(client, {
|
|
540
|
-
patches: [
|
|
768
|
+
patches: [
|
|
769
|
+
{ schema: Docs, entityKey: ENTITY_KEY, set: { title: undefined }, unset: ["title"] },
|
|
770
|
+
],
|
|
541
771
|
})
|
|
542
772
|
expect(sentPatch(writeContract).mutations).toEqual([
|
|
543
|
-
{ name:
|
|
773
|
+
{ name: name("title"), typeId: 0, value: "0x" },
|
|
544
774
|
])
|
|
545
775
|
})
|
|
546
776
|
|
|
777
|
+
it("still rejects null, which is not 'left out'", async () => {
|
|
778
|
+
const { client } = makeClient()
|
|
779
|
+
await expect(
|
|
780
|
+
sendArkivTransaction(client, {
|
|
781
|
+
patches: [{ schema: Docs, entityKey: ENTITY_KEY, set: { level: null } as never }],
|
|
782
|
+
}),
|
|
783
|
+
).rejects.toThrow(MissingValueError)
|
|
784
|
+
})
|
|
785
|
+
|
|
547
786
|
it("sorts writes and tombstones together, not each half on its own", async () => {
|
|
787
|
+
const schema = defineSchema({
|
|
788
|
+
zeta: str(),
|
|
789
|
+
beta: str(),
|
|
790
|
+
alpha: str().optional(),
|
|
791
|
+
gamma: str().optional(),
|
|
792
|
+
})
|
|
548
793
|
const { client, writeContract } = makeClient()
|
|
549
794
|
await sendArkivTransaction(client, {
|
|
550
795
|
patches: [
|
|
551
796
|
{
|
|
797
|
+
schema,
|
|
552
798
|
entityKey: ENTITY_KEY,
|
|
553
799
|
set: { zeta: "z", beta: "b" },
|
|
554
800
|
unset: ["alpha", "gamma"],
|
|
555
|
-
payload: new Uint8Array([1]),
|
|
556
801
|
},
|
|
557
802
|
],
|
|
558
803
|
})
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
toHex("gamma", { size: 32 }),
|
|
565
|
-
toHex("zeta", { size: 32 }),
|
|
804
|
+
expect(sentPatch(writeContract).mutations.map((m) => m.name)).toEqual([
|
|
805
|
+
name("alpha"),
|
|
806
|
+
name("beta"),
|
|
807
|
+
name("gamma"),
|
|
808
|
+
name("zeta"),
|
|
566
809
|
])
|
|
567
810
|
})
|
|
568
811
|
|
|
569
812
|
it("collapses a name repeated in unset, since it is the same intent twice", async () => {
|
|
570
813
|
const { client, writeContract } = makeClient()
|
|
571
814
|
await sendArkivTransaction(client, {
|
|
572
|
-
patches: [{ entityKey: ENTITY_KEY, unset: ["
|
|
815
|
+
patches: [{ schema: Docs, entityKey: ENTITY_KEY, unset: ["title", "title"] }],
|
|
573
816
|
})
|
|
574
817
|
// Two identical tombstones would break the engine's strict-ascending uniqueness rule.
|
|
575
818
|
expect(sentPatch(writeContract).mutations).toHaveLength(1)
|
|
@@ -579,7 +822,9 @@ describe("patch", () => {
|
|
|
579
822
|
const { client, writeContract } = makeClient()
|
|
580
823
|
await expect(
|
|
581
824
|
sendArkivTransaction(client, {
|
|
582
|
-
patches: [
|
|
825
|
+
patches: [
|
|
826
|
+
{ schema: Docs, entityKey: ENTITY_KEY, set: { level: i32(1) }, unset: ["level"] },
|
|
827
|
+
],
|
|
583
828
|
}),
|
|
584
829
|
).rejects.toThrow(ConflictingMutationError)
|
|
585
830
|
expect(writeContract).not.toHaveBeenCalled()
|
|
@@ -588,47 +833,42 @@ describe("patch", () => {
|
|
|
588
833
|
it("rejects a patch with nothing to apply", async () => {
|
|
589
834
|
const { client, writeContract } = makeClient()
|
|
590
835
|
await expect(
|
|
591
|
-
sendArkivTransaction(client, { patches: [{ entityKey: ENTITY_KEY }] }),
|
|
836
|
+
sendArkivTransaction(client, { patches: [{ schema: Docs, entityKey: ENTITY_KEY }] }),
|
|
592
837
|
).rejects.toThrow(EmptyPatchError)
|
|
593
838
|
// Present but empty is just as empty.
|
|
594
|
-
await expect(
|
|
595
|
-
sendArkivTransaction(client, { patches: [{ entityKey: ENTITY_KEY, set: {}, unset: [] }] }),
|
|
596
|
-
).rejects.toThrow(EmptyPatchError)
|
|
597
|
-
expect(writeContract).not.toHaveBeenCalled()
|
|
598
|
-
})
|
|
599
|
-
|
|
600
|
-
it("still validates a content type when one is given", async () => {
|
|
601
|
-
const { client } = makeClient()
|
|
602
839
|
await expect(
|
|
603
840
|
sendArkivTransaction(client, {
|
|
604
|
-
patches: [{ entityKey: ENTITY_KEY,
|
|
841
|
+
patches: [{ schema: Docs, entityKey: ENTITY_KEY, set: {}, unset: [] }],
|
|
605
842
|
}),
|
|
606
|
-
).rejects.toThrow(
|
|
843
|
+
).rejects.toThrow(EmptyPatchError)
|
|
844
|
+
expect(writeContract).not.toHaveBeenCalled()
|
|
607
845
|
})
|
|
608
846
|
|
|
609
847
|
it("rejects a bare string as unset, rather than tombstoning its letters", async () => {
|
|
610
848
|
const { client, writeContract } = makeClient()
|
|
611
|
-
// A Set is built from any iterable, so "
|
|
612
|
-
//
|
|
849
|
+
// A Set is built from any iterable, so "title" would become tombstones for t, i, t, l and e —
|
|
850
|
+
// and the real "title" left untouched.
|
|
613
851
|
await expect(
|
|
614
852
|
sendArkivTransaction(client, {
|
|
615
|
-
patches: [{ entityKey: ENTITY_KEY, unset: "
|
|
853
|
+
patches: [{ schema: Docs, entityKey: ENTITY_KEY, unset: "title" as never }],
|
|
616
854
|
}),
|
|
617
|
-
).rejects.toThrow(/must be an array of
|
|
855
|
+
).rejects.toThrow(/must be an array of column names/)
|
|
618
856
|
expect(writeContract).not.toHaveBeenCalled()
|
|
619
857
|
})
|
|
620
858
|
|
|
621
|
-
it("rejects unsetting a system
|
|
859
|
+
it("rejects unsetting a system attribute, which is no column of any schema", async () => {
|
|
622
860
|
const { client } = makeClient()
|
|
623
861
|
await expect(
|
|
624
|
-
sendArkivTransaction(client, {
|
|
625
|
-
|
|
862
|
+
sendArkivTransaction(client, {
|
|
863
|
+
patches: [{ schema: Docs, entityKey: ENTITY_KEY, unset: ["$owner" as never] }],
|
|
864
|
+
}),
|
|
865
|
+
).rejects.toThrow(SchemaMismatchError)
|
|
626
866
|
})
|
|
627
867
|
|
|
628
868
|
it("does not fetch a block height for a patch-only batch", async () => {
|
|
629
869
|
const { client } = makeClient()
|
|
630
870
|
await sendArkivTransaction(client, {
|
|
631
|
-
patches: [{ entityKey: ENTITY_KEY, set: { level: i32(1) } }],
|
|
871
|
+
patches: [{ schema: Docs, entityKey: ENTITY_KEY, set: { level: i32(1) } }],
|
|
632
872
|
})
|
|
633
873
|
// A patch carries no expiry, so there is nothing to resolve against the chain.
|
|
634
874
|
expect(client.getBlockNumber).not.toHaveBeenCalled()
|
|
@@ -645,7 +885,7 @@ describe("failures that are not reverts", () => {
|
|
|
645
885
|
;(client.waitForTransactionReceipt as ReturnType<typeof vi.fn>).mockRejectedValue(timeout)
|
|
646
886
|
|
|
647
887
|
const failure = await sendArkivTransaction(client, {
|
|
648
|
-
patches: [{ entityKey: ENTITY_KEY, set: { level: i32(1) } }],
|
|
888
|
+
patches: [{ schema: Docs, entityKey: ENTITY_KEY, set: { level: i32(1) } }],
|
|
649
889
|
}).catch((error: Error) => error)
|
|
650
890
|
|
|
651
891
|
expect(failure).toBeInstanceOf(EntityMutationError)
|
|
@@ -672,7 +912,7 @@ describe("a revert the node catches before there is a receipt", () => {
|
|
|
672
912
|
)
|
|
673
913
|
|
|
674
914
|
const failure = await sendArkivTransaction(client, {
|
|
675
|
-
patches: [{ entityKey: ENTITY_KEY, set: { level: i32(1) } }],
|
|
915
|
+
patches: [{ schema: Docs, entityKey: ENTITY_KEY, set: { level: i32(1) } }],
|
|
676
916
|
}).catch((error: Error) => error)
|
|
677
917
|
|
|
678
918
|
expect(failure).toBeInstanceOf(EntityMutationError)
|
|
@@ -689,7 +929,7 @@ describe("a revert the node catches before there is a receipt", () => {
|
|
|
689
929
|
)
|
|
690
930
|
|
|
691
931
|
const failure = await sendArkivTransaction(client, {
|
|
692
|
-
patches: [{ entityKey: ENTITY_KEY, set: { level: i32(1) } }],
|
|
932
|
+
patches: [{ schema: Docs, entityKey: ENTITY_KEY, set: { level: i32(1) } }],
|
|
693
933
|
}).catch((error: Error) => error)
|
|
694
934
|
|
|
695
935
|
expect(failure.message).toContain('"/"')
|
|
@@ -702,7 +942,7 @@ describe("a revert the node catches before there is a receipt", () => {
|
|
|
702
942
|
;(client.writeContract as ReturnType<typeof vi.fn>).mockRejectedValue(opaque)
|
|
703
943
|
|
|
704
944
|
const failure = await sendArkivTransaction(client, {
|
|
705
|
-
patches: [{ entityKey: ENTITY_KEY, set: { level: i32(1) } }],
|
|
945
|
+
patches: [{ schema: Docs, entityKey: ENTITY_KEY, set: { level: i32(1) } }],
|
|
706
946
|
}).catch((error: Error) => error)
|
|
707
947
|
|
|
708
948
|
expect(failure.message).toContain("nonce too low")
|