diff --git a/packages/upload-client/src/blob.js b/packages/upload-client/src/blob.js index a35d0d089..654a6606e 100644 --- a/packages/upload-client/src/blob.js +++ b/packages/upload-client/src/blob.js @@ -196,6 +196,7 @@ export async function add( }, }, proofs, + nonce: options.nonce, }) .execute(conn) }, @@ -341,6 +342,7 @@ export async function list( cursor: options.cursor, size: options.size, }, + nonce: options.nonce, }) .execute(conn) @@ -389,6 +391,7 @@ export async function remove( digest: multihash.bytes, }, proofs, + nonce: options.nonce, }) .execute(conn) diff --git a/packages/upload-client/src/store.js b/packages/upload-client/src/store.js index 58d15b713..375221c97 100644 --- a/packages/upload-client/src/store.js +++ b/packages/upload-client/src/store.js @@ -66,6 +66,7 @@ export async function add( with: SpaceDID.from(resource), nb: { link, size: bytes.length }, proofs, + nonce: options.nonce, }) .execute(conn) }, @@ -185,6 +186,7 @@ export async function get( with: SpaceDID.from(resource), nb: { link }, proofs, + nonce: options.nonce, }) .execute(conn) }, @@ -240,6 +242,7 @@ export async function list( size: options.size, pre: options.pre, }, + nonce: options.nonce, }) .execute(conn) @@ -286,6 +289,7 @@ export async function remove( with: SpaceDID.from(resource), nb: { link }, proofs, + nonce: options.nonce, }) .execute(conn) diff --git a/packages/upload-client/src/types.ts b/packages/upload-client/src/types.ts index b72af7272..a3990e28f 100644 --- a/packages/upload-client/src/types.ts +++ b/packages/upload-client/src/types.ts @@ -272,6 +272,7 @@ export interface RequestOptions Connectable, UploadProgressTrackable { fetch?: typeof globalThis.fetch + nonce?: string } export interface ListRequestOptions extends RequestOptions, Pageable {} diff --git a/packages/upload-client/src/upload.js b/packages/upload-client/src/upload.js index 6b484ba02..7322c8c72 100644 --- a/packages/upload-client/src/upload.js +++ b/packages/upload-client/src/upload.js @@ -46,6 +46,7 @@ export async function add( with: SpaceDID.from(resource), nb: { root, shards }, proofs, + nonce: options.nonce, }) .execute(conn) }, @@ -103,6 +104,7 @@ export async function get( with: SpaceDID.from(resource), nb: { root }, proofs, + nonce: options.nonce, }) .execute(conn) }, @@ -159,6 +161,7 @@ export async function list( size: options.size, pre: options.pre, }, + nonce: options.nonce, }) .execute(conn) @@ -205,6 +208,7 @@ export async function remove( with: SpaceDID.from(resource), nb: { root }, proofs, + nonce: options.nonce, }) .execute(conn) diff --git a/packages/w3up-client/package.json b/packages/w3up-client/package.json index 84fa6dd16..3e833cccc 100644 --- a/packages/w3up-client/package.json +++ b/packages/w3up-client/package.json @@ -100,7 +100,7 @@ "prepare": "npm run build", "test": "npm-run-all -p -r mock:* test:all", "test:all": "run-s test:node test:browser", - "test:node": "hundreds -r html -r text mocha 'test/**/!(*.browser).test.js' -n experimental-vm-modules -n no-warnings -n stack-trace-limit=1000", + "test:node": "hundreds -r html -r text mocha 'test/**/!(*.browser).test.js' -n experimental-vm-modules -n no-warnings -n stack-trace-limit=1000 -t 10000", "test:browser": "playwright-test --runner mocha 'test/**/!(*.node).test.js'", "mock": "run-p mock:*", "mock:bucket-200": "PORT=8989 STATUS=200 node test/helpers/bucket-server.js", diff --git a/packages/w3up-client/src/account.js b/packages/w3up-client/src/account.js index d80a4ffdf..ea2057cd9 100644 --- a/packages/w3up-client/src/account.js +++ b/packages/w3up-client/src/account.js @@ -207,9 +207,13 @@ export class AccountPlan { /** * Gets information about the plan associated with this account. + * + * @param {object} [options] + * @param {string} [options.nonce] */ - async get() { + async get(options) { return await Plan.get(this.model, { + ...options, account: this.model.id, proofs: this.model.proofs, }) @@ -219,9 +223,12 @@ export class AccountPlan { * Sets the plan associated with this account. * * @param {import('@ucanto/interface').DID} productDID + * @param {object} [options] + * @param {string} [options.nonce] */ - async set(productDID) { + async set(productDID, options) { return await Plan.set(this.model, { + ...options, account: this.model.id, product: productDID, proofs: this.model.proofs, @@ -232,17 +239,27 @@ export class AccountPlan { * * @param {import('@web3-storage/access').AccountDID} accountDID * @param {string} returnURL - * @returns + * @param {object} [options] + * @param {string} [options.nonce] */ - async createAdminSession(accountDID, returnURL) { + async createAdminSession(accountDID, returnURL, options) { return await Plan.createAdminSession(this.model, { + ...options, account: accountDID, returnURL, }) } - async subscriptions() { + /** + * + * @param {object} [options] + * @param {string} [options.nonce] + + */ + + async subscriptions(options) { return await Subscription.list(this.model, { + ...options, account: this.model.id, proofs: this.model.proofs, }) diff --git a/packages/w3up-client/src/capability/filecoin.js b/packages/w3up-client/src/capability/filecoin.js index 9a5c8e8d1..28182267e 100644 --- a/packages/w3up-client/src/capability/filecoin.js +++ b/packages/w3up-client/src/capability/filecoin.js @@ -11,10 +11,13 @@ export class FilecoinClient extends Base { * * @param {import('multiformats').UnknownLink} content * @param {import('@web3-storage/capabilities/types').PieceLink} piece + * @param {object} [options] + * @param {string} [options.nonce] */ - async offer(content, piece) { + async offer(content, piece, options) { const conf = await this._invocationConfig([FilecoinCapabilities.offer.can]) return Storefront.filecoinOffer(conf, content, piece, { + ...options, connection: this._serviceConf.filecoin, }) } @@ -23,10 +26,13 @@ export class FilecoinClient extends Base { * Request info about a content piece in Filecoin deals * * @param {import('@web3-storage/capabilities/types').PieceLink} piece + * @param {object} [options] + * @param {string} [options.nonce] */ - async info(piece) { + async info(piece, options) { const conf = await this._invocationConfig([FilecoinCapabilities.info.can]) return Storefront.filecoinInfo(conf, piece, { + ...options, connection: this._serviceConf.filecoin, }) } diff --git a/packages/w3up-client/src/capability/plan.js b/packages/w3up-client/src/capability/plan.js index 85782b200..679487f14 100644 --- a/packages/w3up-client/src/capability/plan.js +++ b/packages/w3up-client/src/capability/plan.js @@ -6,9 +6,11 @@ export class PlanClient extends Base { /** * * @param {import('@web3-storage/access').AccountDID} account + * @param {object} [options] + * @param {string} [options.nonce] */ - async get(account) { - const out = await get({ agent: this.agent }, { account }) + async get(account, options) { + const out = await get({ agent: this.agent }, { ...options, account }) if (!out.ok) { throw new Error(`failed ${Plan.get.can} invocation`, { @@ -22,9 +24,14 @@ export class PlanClient extends Base { * * @param {API.AccountDID} account * @param {API.DID} product + * @param {object} [options] + * @param {string} [options.nonce] */ - async set(account, product) { - const out = await set({ agent: this.agent }, { account, product }) + async set(account, product, options) { + const out = await set( + { agent: this.agent }, + { ...options, account, product } + ) if (!out.ok) { throw new Error(`failed ${Plan.set.can} invocation`, { cause: out.error, @@ -37,11 +44,13 @@ export class PlanClient extends Base { * * @param {API.AccountDID} account * @param {string} returnURL + * @param {object} [options] + * @param {string} [options.nonce] */ - async createAdminSession(account, returnURL) { + async createAdminSession(account, returnURL, options) { const out = await createAdminSession( { agent: this.agent }, - { account, returnURL } + { ...options, account, returnURL } ) if (!out.ok) { throw new Error(`failed ${Plan.createAdminSession.can} invocation`, { @@ -58,12 +67,14 @@ export class PlanClient extends Base { * @param {{agent: API.Agent}} client * @param {object} options * @param {API.AccountDID} options.account + * @param {string} [options.nonce] * @param {API.Delegation[]} [options.proofs] */ -export const get = async ({ agent }, { account, proofs = [] }) => { +export const get = async ({ agent }, { account, nonce, proofs = [] }) => { const receipt = await agent.invokeAndExecute(Plan.get, { with: account, proofs, + nonce, }) return receipt.out } @@ -75,12 +86,17 @@ export const get = async ({ agent }, { account, proofs = [] }) => { * @param {object} options * @param {API.DID} options.product * @param {API.AccountDID} options.account + * @param {string} [options.nonce] * @param {API.Delegation[]} [options.proofs] */ -export const set = async ({ agent }, { account, product, proofs = [] }) => { +export const set = async ( + { agent }, + { account, product, nonce, proofs = [] } +) => { const receipt = await agent.invokeAndExecute(Plan.set, { with: account, nb: { product }, + nonce, proofs, }) return receipt.out @@ -96,15 +112,17 @@ export const set = async ({ agent }, { account, product, proofs = [] }) => { * @param {object} options * @param {API.AccountDID} options.account * @param {string} options.returnURL + * @param {string} [options.nonce] * @param {API.Delegation[]} [options.proofs] */ export const createAdminSession = async ( { agent }, - { account, returnURL, proofs = [] } + { account, returnURL, nonce, proofs = [] } ) => { const receipt = await agent.invokeAndExecute(Plan.createAdminSession, { with: account, proofs, + nonce, nb: { returnURL, }, diff --git a/packages/w3up-client/src/capability/space.js b/packages/w3up-client/src/capability/space.js index f2c077d28..e660812fa 100644 --- a/packages/w3up-client/src/capability/space.js +++ b/packages/w3up-client/src/capability/space.js @@ -8,8 +8,10 @@ export class SpaceClient extends Base { * Get information about a space. * * @param {import('../types.js').DID} space - DID of the space to retrieve info about. + * @param {object} [options] + * @param {string} [options.nonce] */ - async info(space) { - return await this._agent.getSpaceInfo(space) + async info(space, options) { + return await this._agent.getSpaceInfo(space, options) } } diff --git a/packages/w3up-client/src/capability/subscription.js b/packages/w3up-client/src/capability/subscription.js index 373b2e2f7..ce6baf9a3 100644 --- a/packages/w3up-client/src/capability/subscription.js +++ b/packages/w3up-client/src/capability/subscription.js @@ -10,10 +10,12 @@ export class SubscriptionClient extends Base { * List subscriptions for the passed account. * * @param {import('@web3-storage/access').AccountDID} account + * @param {object} [options] + * @param {string} [options.nonce] */ /* c8 ignore next */ - async list(account) { - const out = await list({ agent: this.agent }, { account }) + async list(account, options) { + const out = await list({ agent: this.agent }, { ...options, account }) /* c8 ignore next 8 */ if (!out.ok) { throw new Error( @@ -34,13 +36,15 @@ export class SubscriptionClient extends Base { * @param {{agent: API.Agent}} client * @param {object} options * @param {API.AccountDID} options.account + * @param {string} [options.nonce] * @param {API.Delegation[]} [options.proofs] */ -export const list = async ({ agent }, { account, proofs = [] }) => { +export const list = async ({ agent }, { account, nonce, proofs = [] }) => { const receipt = await agent.invokeAndExecute(SubscriptionCapabilities.list, { with: account, proofs, nb: {}, + nonce, }) return receipt.out } diff --git a/packages/w3up-client/src/capability/usage.js b/packages/w3up-client/src/capability/usage.js index ebefc46b3..81cf5aa84 100644 --- a/packages/w3up-client/src/capability/usage.js +++ b/packages/w3up-client/src/capability/usage.js @@ -11,9 +11,14 @@ export class UsageClient extends Base { * * @param {import('../types.js').SpaceDID} space * @param {{ from: Date, to: Date }} period + * @param {object} [options] + * @param {string} [options.nonce] */ - async report(space, period) { - const out = await report({ agent: this.agent }, { space, period }) + async report(space, period, options) { + const out = await report( + { agent: this.agent }, + { ...options, space, period } + ) /* c8 ignore next 7 */ if (!out.ok) { throw new Error(`failed ${UsageCapabilities.report.can} invocation`, { @@ -32,13 +37,18 @@ export class UsageClient extends Base { * @param {object} options * @param {API.SpaceDID} options.space * @param {{ from: Date, to: Date }} options.period + * @param {string} [options.nonce] * @param {API.Delegation[]} [options.proofs] * @returns {Promise>} */ -export const report = async ({ agent }, { space, period, proofs = [] }) => { +export const report = async ( + { agent }, + { space, period, nonce, proofs = [] } +) => { const receipt = await agent.invokeAndExecute(UsageCapabilities.report, { with: space, proofs, + nonce, nb: { period: { from: Math.floor(period.from.getTime() / 1000), diff --git a/packages/w3up-client/test/account.test.js b/packages/w3up-client/test/account.test.js index 19cf52541..b9a1a2650 100644 --- a/packages/w3up-client/test/account.test.js +++ b/packages/w3up-client/test/account.test.js @@ -240,13 +240,13 @@ export const testAccount = Test.withContext({ ) ) - const { ok: plan } = await account.plan.get() + const { ok: plan } = await account.plan.get({ nonce: '2' }) assert.ok(plan?.product, 'did:web:free.web3.storage') Result.unwrap(await account.plan.set('did:web:lite.web3.storage')) - const { ok: newPlan } = await account.plan.get() + const { ok: newPlan } = await account.plan.get({ nonce: '3' }) assert.ok(newPlan?.product, 'did:web:lite.web3.storage') }, diff --git a/packages/w3up-client/test/capability/filecoin.test.js b/packages/w3up-client/test/capability/filecoin.test.js index 8a787e312..f2640bece 100644 --- a/packages/w3up-client/test/capability/filecoin.test.js +++ b/packages/w3up-client/test/capability/filecoin.test.js @@ -1,6 +1,5 @@ import { Filecoin as FilecoinCapabilities } from '@web3-storage/capabilities' import { randomAggregate, randomCAR, randomCargo } from '../helpers/random.js' -import { Receipt, Message } from '@ucanto/core' import * as Test from '../test.js' export const FilecoinClient = Test.withContext({ @@ -26,7 +25,7 @@ export const FilecoinClient = Test.withContext({ info: { 'should get piece info': async ( assert, - { client: alice, pieceStore, service, agentStore } + { client: alice, pieceStore, service, receiptStore } ) => { const { pieces, aggregate } = await randomAggregate(10, 100) const content = await randomCAR(100) @@ -65,26 +64,19 @@ export const FilecoinClient = Test.withContext({ .delegate() // @ts-ignore - await agentStore.messages.write( - await Message.build({ - receipts: [ - await Receipt.issue({ - issuer: service.signer, - ran: pieceAcceptInvocation.link(), - result: { - ok: { - piece: cargo.link, - aggregate: aggregate.link, - inclusion: { - subtree: proof.ok[0], - index: proof.ok[1], - }, - }, - }, - }), - ], - }) - ) + await receiptStore.put({ + ran: pieceAcceptInvocation.link(), + out: { + ok: { + piece: cargo.link, + aggregate: aggregate.link, + inclusion: { + subtree: proof.ok[0], + index: proof.ok[1], + }, + }, + }, + }) const res = await alice.capability.filecoin.info(cargo.link) assert.deepEqual(res.out.ok?.piece.toString(), cargo.link.toString()) diff --git a/packages/w3up-client/test/capability/plan.test.js b/packages/w3up-client/test/capability/plan.test.js index 04dc47ca8..f75cbc89b 100644 --- a/packages/w3up-client/test/capability/plan.test.js +++ b/packages/w3up-client/test/capability/plan.test.js @@ -37,7 +37,9 @@ export const PlanClient = Test.withContext({ ) ) - const res = await client.capability.plan.get(account.did()) + const res = await client.capability.plan.get(account.did(), { + nonce: 'retry', + }) assert.equal(res.product, exampleProduct) assert.ok(res.updatedAt) @@ -73,16 +75,22 @@ export const PlanClient = Test.withContext({ (await client.capability.plan.get(account.did())).product, initialProduct ) - assert.ok(await client.capability.plan.set(account.did(), updatedProduct)) + assert.ok( + await client.capability.plan.set(account.did(), updatedProduct, { + nonce: '2', + }) + ) assert.equal( - (await client.capability.plan.get(account.did())).product, + (await client.capability.plan.get(account.did(), { nonce: '2' })) + .product, updatedProduct ) await assert.rejects( client.capability.plan.set( 'did:mailto:example.com:notauser', - initialProduct + initialProduct, + { nonce: '3' } ) ) }, @@ -113,14 +121,16 @@ export const PlanClient = Test.withContext({ const session = await client.capability.plan.createAdminSession( account.did(), - 'https://example.com/return-url' + 'https://example.com/return-url', + { nonce: '2' } ) assert.ok(session.url) await assert.rejects( client.capability.plan.createAdminSession( 'did:mailto:example.com:notauser', - 'https://example.com/return-url' + 'https://example.com/return-url', + { nonce: '3' } ) ) }, diff --git a/packages/w3up-client/test/capability/subscription.test.js b/packages/w3up-client/test/capability/subscription.test.js index b3d8f1408..c4d036712 100644 --- a/packages/w3up-client/test/capability/subscription.test.js +++ b/packages/w3up-client/test/capability/subscription.test.js @@ -26,7 +26,9 @@ export const SubscriptionClient = Test.withContext({ assert.ok(result.ok) assert.deepEqual( - await client.capability.subscription.list(account.did()), + await client.capability.subscription.list(account.did(), { + nonce: 'retry', + }), { results: [ { diff --git a/packages/w3up-client/test/capability/upload.test.js b/packages/w3up-client/test/capability/upload.test.js index 5b9a10e6c..573221908 100644 --- a/packages/w3up-client/test/capability/upload.test.js +++ b/packages/w3up-client/test/capability/upload.test.js @@ -34,7 +34,7 @@ export const UploadClient = Test.withContext({ list: { 'should list uploads': async ( assert, - { client: alice, service, provisionsStorage } + { client: alice, service, provisionsStorage, uploadTable } ) => { const car = await randomCAR(128) @@ -57,9 +57,13 @@ export const UploadClient = Test.withContext({ await alice.capability.upload.add(car.roots[0], [car.cid]) - const { - results: [entry], - } = await alice.capability.upload.list() + assert.deepEqual(await uploadTable.exists(space.did(), car.roots[0]), { + ok: true, + }) + + const list = await alice.capability.upload.list({ nonce: 'retry' }) + + const [entry] = list.results assert.deepEqual(entry.root, car.roots[0]) assert.deepEqual(entry.shards, [car.cid])