Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .nx/version-plans/version-plan-1769007491408.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@storacha/capabilities': minor
---

add 'account/egress/get' capability
62 changes: 62 additions & 0 deletions packages/capabilities/src/account/egress.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { capability, fail, ok, Schema } from '@ucanto/validator'
import {
AccountDID,
SpaceDID,
equalWith,
and,
equal,
containedWithin,
} from '../utils.js'

/**
* Capability can only be delegated (but not invoked) allowing audience to
* be derived any `account/egress/` prefixed capability for the account identified
* by DID in the `with` field.
*/
export const accountEgress = capability({
can: 'account/egress/*',
with: AccountDID,
derives: equalWith,
})

/**
* Capability can be invoked by an agent to retrieve egress data for all or a
* specified set of spaces within an account in a given period.
*/
export const get = capability({
can: 'account/egress/get',
with: AccountDID,
nb: Schema.struct({
spaces: SpaceDID.array().optional(),
/** Period to retrieve egress between. */
period: Schema.struct({
/** Date-only string in ISO 8601 format (inclusive). */
from: Schema.string(),
/** Date-only string in ISO 8601 format (exclusive). */
to: Schema.string(),
}).optional(),
}),
derives: (child, parent) => {
return (
and(equalWith(child, parent)) ||
and(
(() => {
if (parent.nb.spaces === undefined) {
return ok({})
}
if (child.nb.spaces === undefined) {
return fail(
`Constraint violation: violates imposed spaces constraint ${parent.nb.spaces} because it asks for all spaces`
)
}
return containedWithin(child.nb.spaces, parent.nb.spaces, 'spaces')
})()
) ||
and(
equal(child.nb.period?.from, parent.nb.period?.from, 'period.from')
) ||
and(equal(child.nb.period?.to, parent.nb.period?.to, 'period.to')) ||
ok({})
)
},
})
3 changes: 3 additions & 0 deletions packages/capabilities/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import * as SpaceBlob from './space/blob.js'
import * as W3sBlob from './web3.storage/blob.js'
import * as HTTP from './http.js'
import * as AccountUsage from './account/usage.js'
import * as AccountEgress from './account/egress.js'
import * as PDP from './pdp.js'

export {
Expand Down Expand Up @@ -59,6 +60,7 @@ export {
W3sBlob,
HTTP,
AccountUsage,
AccountEgress,
PDP,
}

Expand Down Expand Up @@ -132,6 +134,7 @@ export const abilitiesAsStrings = [
SpaceIndex.index.can,
SpaceIndex.add.can,
AccountUsage.get.can,
AccountEgress.get.can,
PDP.accept.can,
PDP.info.can,
]
46 changes: 46 additions & 0 deletions packages/capabilities/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ import * as UCANCaps from './ucan.js'
import * as PlanCaps from './plan.js'
import * as UsageCaps from './usage.js'
import * as AccountUsageCaps from './account/usage.js'
import * as AccountEgressCaps from './account/egress.js'
import * as PDPCaps from './pdp.js'

export type ISO8601Date = string
Expand Down Expand Up @@ -259,6 +260,49 @@ export interface SpaceEgressUsage {
providers: Record<ProviderDID, EgressUsageData>
}

// AccountEgress
export type AccountEgress = InferInvokedCapability<
typeof AccountEgressCaps.accountEgress
>
export type AccountEgressGet = InferInvokedCapability<
typeof AccountEgressCaps.get
>

export interface AccountNotFoundError extends Ucanto.Failure {
name: 'AccountNotFound'
}

export interface SpaceUnauthorizedError extends Ucanto.Failure {
name: 'SpaceUnauthorized'
}

export interface PeriodNotAcceptableError extends Ucanto.Failure {
name: 'PeriodNotAcceptable'
}

export type AccountEgressGetFailure = AccountNotFoundError | SpaceUnauthorizedError | PeriodNotAcceptableError | Ucanto.Failure

export interface AccountEgressGetSuccess {
// total egress across all spaces in the period in bytes
total: number
// breakdown by space
spaces: Record<SpaceDID, SpaceEgress>
}

export interface SpaceEgress {
// total egress for the space in the period
total: number
// daily egress stats
dailyStats: DailyStats[]
}

export interface DailyStats {
// date the stats refer to
date: ISO8601Date
// egress for the day in bytes
egress: number
}

// Provider
export type ProviderAdd = InferInvokedCapability<typeof provider.add>
// eslint-disable-next-line @typescript-eslint/no-empty-interface
Expand Down Expand Up @@ -1240,6 +1284,8 @@ export type ServiceAbilityArray = [
SpaceIndexAdd['can'],
AccountUsage['can'],
AccountUsageGet['can'],
AccountEgress['can'],
AccountEgressGet['can'],
PDPAccept['can'],
PDPInfo['can']
]
Expand Down
Loading
Loading