Skip to content

Commit

Permalink
feat: add static agent constructor methods
Browse files Browse the repository at this point in the history
  • Loading branch information
alanshaw committed Dec 6, 2022
1 parent 144048b commit b6e0ff0
Show file tree
Hide file tree
Showing 8 changed files with 85 additions and 81 deletions.
15 changes: 10 additions & 5 deletions packages/access-client/src/agent-data.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,7 @@ import { CID } from 'multiformats'

/** @typedef {import('./types').AgentDataModel} AgentDataModel */

/**
* @implements {AgentDataModel}
*/
/** @implements {AgentDataModel} */
export class AgentData {
/** @type {(data: import('./types').AgentDataExport) => Promise<void> | void} */
#save
Expand All @@ -26,6 +24,8 @@ export class AgentData {
}

/**
* Create a new AgentData instance from the passed initialization data.
*
* @param {Partial<import('./types').AgentDataModel>} [init]
* @param {import('./types').AgentDataOptions} [options]
*/
Expand Down Expand Up @@ -67,6 +67,8 @@ export class AgentData {
}

/**
* Instantiate AgentData from previously exported data.
*
* @param {import('./types').AgentDataExport} raw
* @param {import('./types').AgentDataOptions} [options]
*/
Expand Down Expand Up @@ -99,6 +101,9 @@ export class AgentData {
)
}

/**
* Export data in a format safe to pass to `structuredClone()`.
*/
export() {
/** @type {import('./types').AgentDataExport} */
const raw = {
Expand Down Expand Up @@ -127,7 +132,7 @@ export class AgentData {
*/
async addSpace(did, meta, proof) {
this.spaces.set(did, meta)
await (proof ? this.addDelegation(proof) : this.#save(this.export()));
await (proof ? this.addDelegation(proof) : this.#save(this.export()))
}

/**
Expand All @@ -145,7 +150,7 @@ export class AgentData {
async addDelegation(delegation, meta) {
this.delegations.set(delegation.cid.toString(), {
delegation,
...(meta ? { meta } : {}),
meta: meta ?? {},
})
await this.#save(this.export())
}
Expand Down
66 changes: 50 additions & 16 deletions packages/access-client/src/agent.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ import {
validate,
canDelegateCapability,
} from './delegations.js'
import { AgentData } from './agent-data.js'

export { AgentData }

const HOST = 'https://w3access-staging.protocol-labs.workers.dev'
const PRINCIPAL = DID.parse(
Expand Down Expand Up @@ -80,16 +83,47 @@ export class Agent {
*/
constructor(data, options = {}) {
this.url = options.url ?? new URL(HOST)
this.connection = options.connection ?? connection({
principal: options.servicePrincipal,
url: this.url
})
this.connection =
options.connection ??
connection({
principal: options.servicePrincipal,
url: this.url,
})
this.#data = data
this.#service = undefined
}

/**
* Create a new Agent instance, optionally with the passed initialization data.
*
* @param {Partial<import('./types').AgentDataModel>} [init]
* @param {import('./types').AgentOptions & { store?: import('./types').IStore<import('./types').AgentDataExport> }} [options]
*/
static async create(init, options = {}) {
const { store } = options
if (store) await store.open()
const data = await AgentData.create(init, { store })
return new Agent(data, options)
}

/**
* Create a new Agent instance from pre-exported agent data.
*
* @param {import('./types').AgentDataExport} raw
* @param {import('./types').AgentOptions & { store?: import('./types').IStore<import('./types').AgentDataExport> }} [options]
*/
static async from(raw, options = {}) {
const { store } = options
const data = AgentData.fromExport(raw, { store })
return new Agent(data, options)
}

get issuer() {
return this.data.principal
return this.#data.principal
}

get meta() {
return this.#data.meta
}

async service() {
Expand All @@ -103,7 +137,7 @@ export class Agent {
}

did() {
return this.data.principal.did()
return this.#data.principal.did()
}

/**
Expand All @@ -118,7 +152,7 @@ export class Agent {
checkAudience: this.issuer,
checkIsExpired: true,
})
await this.data.addDelegation(delegation, { audience: this.meta })
await this.#data.addDelegation(delegation, { audience: this.meta })
}

/**
Expand All @@ -128,7 +162,7 @@ export class Agent {
*/
async *#delegations(caps) {
const _caps = new Set(caps)
for (const [, value] of this.data.delegations) {
for (const [, value] of this.#data.delegations) {
// check expiration
if (!isExpired(value.delegation)) {
// check if delegation can be used
Expand All @@ -147,7 +181,7 @@ export class Agent {
}
} else {
// delete any expired delegation
await this.data.removeDelegation(value.delegation.cid)
await this.#data.removeDelegation(value.delegation.cid)
}
}
}
Expand Down Expand Up @@ -210,7 +244,7 @@ export class Agent {
})

const meta = { name, isRegistered: false }
await this.data.addSpace(signer.did(), meta, proof)
await this.#data.addSpace(signer.did(), meta, proof)

return {
did: signer.did(),
Expand Down Expand Up @@ -312,7 +346,7 @@ export class Agent {
throw new Error(`Agent has no proofs for ${space}.`)
}

await this.data.setCurrentSpace(space)
await this.#data.setCurrentSpace(space)

return space
}
Expand All @@ -321,14 +355,14 @@ export class Agent {
* Get current space DID
*/
currentSpace() {
return this.data.currentSpace
return this.#data.currentSpace
}

/**
* Get current space DID, proofs and abilities
*/
async currentSpaceWithMeta() {
if (!this.data.currentSpace) {
if (!this.#data.currentSpace) {
return
}

Expand Down Expand Up @@ -426,8 +460,8 @@ export class Agent {

spaceMeta.isRegistered = true

this.data.addSpace(space, spaceMeta)
this.data.removeDelegation(voucherRedeem.cid)
this.#data.addSpace(space, spaceMeta)
this.#data.removeDelegation(voucherRedeem.cid)
}

/**
Expand Down Expand Up @@ -492,7 +526,7 @@ export class Agent {
...options,
})

await this.data.addDelegation(delegation, {
await this.#data.addDelegation(delegation, {
audience: options.audienceMeta,
})

Expand Down
13 changes: 2 additions & 11 deletions packages/access-client/src/stores/store-conf.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,26 +38,17 @@ export class StoreConf {
this.path = this.#config.path
}

/**
* @returns {Promise<Store<T>>}
*/
async open() {
return this
}
async open() {}

async close() {}

async reset() {
this.#config.clear()
}

/**
* @param {T} data
* @returns {Promise<Store<T>>}
*/
/** @param {T} data */
async save(data) {
this.#config.set(data)
return this
}

/** @returns {Promise<T|undefined>} */
Expand Down
18 changes: 6 additions & 12 deletions packages/access-client/src/stores/store-indexeddb.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,14 +45,11 @@ export class StoreIndexedDB {
this.#dbStoreName = options.dbStoreName ?? STORE_NAME
}

/**
* @returns {Promise<Store<T>>}
*/
async open() {
const db = this.#db
if (db) return this
if (db) return

/** @type {import('p-defer').DeferredPromise<Store<T>>} */
/** @type {import('p-defer').DeferredPromise<void>} */
const { resolve, reject, promise } = defer()
const openReq = indexedDB.open(this.#dbName, this.#dbVersion)

Expand All @@ -63,7 +60,7 @@ export class StoreIndexedDB {

openReq.addEventListener('success', () => {
this.#db = openReq.result
resolve(this)
resolve()
})

openReq.addEventListener('error', () => reject(openReq.error))
Expand All @@ -79,10 +76,7 @@ export class StoreIndexedDB {
this.#db = undefined
}

/**
* @param {T} data
* @returns {Promise<Store<T>>}
*/
/** @param {T} data */
async save(data) {
const db = this.#db
if (!db) throw new Error('Store is not open')
Expand All @@ -92,10 +86,10 @@ export class StoreIndexedDB {
'readwrite',
this.#dbStoreName,
async (store) => {
/** @type {import('p-defer').DeferredPromise<Store<T>>} */
/** @type {import('p-defer').DeferredPromise<void>} */
const { resolve, reject, promise } = defer()
const putReq = store.put({ id: DATA_ID, ...data })
putReq.addEventListener('success', () => resolve(this))
putReq.addEventListener('success', () => resolve())
putReq.addEventListener('error', () =>
reject(new Error('failed to query DB', { cause: putReq.error }))
)
Expand Down
8 changes: 3 additions & 5 deletions packages/access-client/src/stores/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,19 @@ export interface IStore<T> {
/**
* Open store
*/
open: () => Promise<IStore<T>>
open: () => Promise<void>
/**
* Clean up and close store
*/
close: () => Promise<void>
/**
* Persist data to the store's backend
*
* @param data
*/
save: (data: T) => Promise<IStore<T>>
save: (data: T) => Promise<void>
/**
* Loads data from the store's backend
*/
load: () => Promise<T|undefined>
load: () => Promise<T | undefined>
/**
* Clean all the data in the store's backend
*/
Expand Down
4 changes: 2 additions & 2 deletions packages/access-client/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ export type AgentDataExport = Pick<
delegations: Map<
CIDString,
{
meta?: DelegationMeta
meta: DelegationMeta
delegation: Array<{ cid: CIDString; bytes: Uint8Array }>
}
>
Expand All @@ -122,7 +122,7 @@ export interface DelegationMeta {
* Audience metadata to be easier to build UIs with human readable data
* Normally used with delegations issued to third parties or other devices.
*/
audience: AgentMeta
audience?: AgentMeta
}

/**
Expand Down
Loading

0 comments on commit b6e0ff0

Please sign in to comment.