Skip to content

Commit

Permalink
feat: update vue and solid keyrings
Browse files Browse the repository at this point in the history
  • Loading branch information
travis committed Nov 13, 2023
1 parent 9e93aea commit ed7409b
Show file tree
Hide file tree
Showing 6 changed files with 137 additions and 101 deletions.
2 changes: 1 addition & 1 deletion packages/keyring-core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
"@web3-storage/did-mailto": "^2.0.2",
"@web3-storage/filecoin-client": "^3.1.0",
"@web3-storage/upload-client": "^12.0.0",
"@web3-storage/w3up-client": "10.0.0"
"@web3-storage/w3up-client": "10.1.0"
},
"eslintConfig": {
"extends": [
Expand Down
8 changes: 6 additions & 2 deletions packages/keyring-core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import type {
Proof,
Principal,
Delegation,
UCANOptions
UCANOptions,
Signer
} from '@ucanto/interface'
import { StoreIndexedDB, getAccountPlan } from '@web3-storage/access'
import * as Ucanto from '@ucanto/interface'
Expand All @@ -31,6 +32,10 @@ export interface KeyringContextState {
* Spaces available to this agent.
*/
spaces: Space[]
/**
* The current user agent (this device).
*/
agent?: Signer
/**
* The w3up client representing the current user agent (this device).
*/
Expand Down Expand Up @@ -141,7 +146,6 @@ export async function createClient (
})
}

// @ts-expect-error remove once new w3up ships
export const useAccount = (client: Client, { email }: { email?: string }): W3Account.Account | undefined => {
const accounts = Object.values(W3Account.list(client))
return accounts.find((account) => account.toEmail() === email)
Expand Down
10 changes: 8 additions & 2 deletions packages/react-keyring/src/providers/Keyring.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import type {
Plan,
PlanGetResult,
ServiceConfig,
Space,
Space
} from '@w3ui/keyring-core'
import type {
Capability,
Expand All @@ -16,6 +16,7 @@ import type {
DIDKey,
Principal,
Proof,
Signer
} from '@ucanto/interface'

import React, { createContext, useState, useContext } from 'react'
Expand All @@ -39,6 +40,7 @@ export const keyringContextDefaultValue: KeyringContextValue = [
{
space: undefined,
spaces: [],
agent: undefined,
client: undefined,
account: undefined
},
Expand Down Expand Up @@ -79,6 +81,7 @@ export function KeyringProvider ({
connection
}: KeyringProviderProps): JSX.Element {
const [client, setClient] = useState<Client>()
const [agent, setAgent] = useState<Signer>()
const [account, setAccount] = useLocalStorageState<string>(W3UI_ACCOUNT_LOCALSTORAGE_KEY)
const [space, setSpace] = useState<Space>()
const [spaces, setSpaces] = useState<Space[]>([])
Expand All @@ -89,6 +92,7 @@ export function KeyringProvider ({
if (client == null) {
const client = await createClient({ servicePrincipal, connection })
setClient(client)
setAgent(client.agent.issuer)
setSpace(client.currentSpace())
setSpaces(client.spaces())
return client
Expand Down Expand Up @@ -137,6 +141,7 @@ export function KeyringProvider ({
const c = await getClient()
const account = useAccount(c, { email })
const space = c.currentSpace()
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
if (account && space) {
await account.provision(space.did() as DIDKey)
setSpace(c.currentSpace())
Expand Down Expand Up @@ -164,7 +169,7 @@ export function KeyringProvider ({

const resetAgent = async (): Promise<void> => {
const c = await getClient()
// @ts-ignore store is there but the type doesn't expose it - TODO add store to Agent type
// @ts-expect-error store is there but the type doesn't expose it - TODO add store to Agent type
await Promise.all([c.agent.store.reset(), unloadAgent()])
}

Expand Down Expand Up @@ -204,6 +209,7 @@ export function KeyringProvider ({
const state = {
space,
spaces,
agent,
client,
account
}
Expand Down
101 changes: 55 additions & 46 deletions packages/solid-keyring/src/providers/Keyring.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ import type {
KeyringContextActions,
ServiceConfig,
CreateDelegationOptions,
Agent,
Abilities,
Email,
PlanGetResult
PlanGetResult,
Client
} from '@w3ui/keyring-core'
import type { Delegation, Capability, DID, Principal } from '@ucanto/interface'
import type { Delegation, Capability, DID, Principal, DIDKey } from '@ucanto/interface'

import {
createContext,
Expand All @@ -19,12 +19,10 @@ import {
} from 'solid-js'
import { createStore } from 'solid-js/store'
import {
authorize as accessAuthorize,
createAgent,
getCurrentSpace as getCurrentSpaceInAgent,
getSpaces,
createClient,
getPlan as getPlanWithAgent,
W3UI_ACCOUNT_LOCALSTORAGE_KEY
W3UI_ACCOUNT_LOCALSTORAGE_KEY,
useAccount
} from '@w3ui/keyring-core'

export { KeyringContextState, KeyringContextActions }
Expand All @@ -38,6 +36,7 @@ const defaultState: KeyringContextState = {
space: undefined,
spaces: [],
agent: undefined,
client: undefined,
account: window.localStorage.getItem(W3UI_ACCOUNT_LOCALSTORAGE_KEY) ?? undefined
}

Expand Down Expand Up @@ -75,40 +74,42 @@ export const KeyringProvider: ParentComponent<KeyringProviderProps> = (
space: defaultState.space,
spaces: defaultState.spaces,
agent: defaultState.agent,
client: defaultState.client,
account: defaultState.account
})

const [agent, setAgent] = createSignal<Agent>()
const [client, setClient] = createSignal<Client>()
const [registerAbortController, setRegisterAbortController] =
createSignal<AbortController>()

const getAgent = async (): Promise<Agent> => {
let a = agent()
if (a == null) {
a = await createAgent({
const getClient = async (): Promise<Client> => {
let c = client()
if (c == null) {
c = await createClient({
servicePrincipal: props.servicePrincipal,
connection: props.connection
})
setAgent(a)
setState('agent', a.issuer)
setState('space', getCurrentSpaceInAgent(a))
setState('spaces', getSpaces(a))
setClient(c)
setState('client', c)
setState('agent', c.agent.issuer)
setState('space', c.currentSpace())
setState('spaces', c.spaces())
}
return a
return c
}

const authorize = async (email: `${string}@${string}`): Promise<void> => {
const agent = await getAgent()
const c = await getClient()
const controller = new AbortController()
setRegisterAbortController(controller)

try {
await accessAuthorize(agent, email, { signal: controller.signal })
await c.authorize(email, { signal: controller.signal })
setState('account', email)
window.localStorage.setItem(W3UI_ACCOUNT_LOCALSTORAGE_KEY, email)
const newSpaces = getSpaces(agent)
const newSpaces = c.spaces()
setState('spaces', newSpaces)
const newCurrentSpace = getCurrentSpaceInAgent(agent) ?? newSpaces[0]
const newCurrentSpace = c.currentSpace() ?? newSpaces[0]
if (newCurrentSpace != null) {
await setCurrentSpace(newCurrentSpace.did() as DID<'key'>)
}
Expand All @@ -127,65 +128,73 @@ export const KeyringProvider: ParentComponent<KeyringProviderProps> = (
}

const createSpace = async (name?: string): Promise<DID> => {
const agent = await getAgent()
const { did } = await agent.createSpace(name)
await agent.setCurrentSpace(did)
setState('space', getCurrentSpaceInAgent(agent))
const c = await getClient()
const space = await c.createSpace(name ?? 'Unnamed Space')
const did = space.did()
await c.setCurrentSpace(did)
setState('space', c.currentSpace())
return did
}

const registerSpace = async (email: string): Promise<void> => {
const agent = await getAgent()
await agent.registerSpace(email)
setState('space', getCurrentSpaceInAgent(agent))
setState('spaces', getSpaces(agent))
const c = await getClient()
const account = useAccount(c, { email })
const space = c.currentSpace()
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
if (account && space) {
await account.provision(space.did() as DIDKey)
setState('space', c.currentSpace())
setState('spaces', c.spaces())
}
}

const setCurrentSpace = async (did: DID): Promise<void> => {
const agent = await getAgent()
await agent.setCurrentSpace(did as DID<'key'>)
setState('space', getCurrentSpaceInAgent(agent))
const c = await getClient()
await c.setCurrentSpace(did as DID<'key'>)
setState('space', c.currentSpace())
}

const loadAgent = async (): Promise<void> => {
if (agent() != null) return
await getAgent()
if (client() != null) return
await getClient()
}

const unloadAgent = async (): Promise<void> => {
setState('space', undefined)
setState('spaces', [])
setState('agent', undefined)
setState('client', undefined)
setState('account', undefined)
setAgent(undefined)
setClient(undefined)
}

const resetAgent = async (): Promise<void> => {
const agent = await getAgent()
await Promise.all([agent.store.reset(), unloadAgent()])
const c = await getClient()
// @ts-expect-error store is there but the type doesn't expose it - TODO add store to Agent type
await Promise.all([c.store.reset(), unloadAgent()])
}

const getProofs = async (caps: Capability[]): Promise<Delegation[]> => {
const agent = await getAgent()
return agent.proofs(caps)
const c = await getClient()
return c.proofs(caps)
}

const getPlan = async (email: Email): Promise<PlanGetResult> => {
const agent = await getAgent()
return await getPlanWithAgent(agent, email)
const c = await getClient()
return await getPlanWithAgent(c, email)
}

const createDelegation = async (
audience: Principal,
abilities: Abilities[],
options: CreateDelegationOptions
): Promise<Delegation> => {
const agent = await getAgent()
const c = await getClient()
const audienceMeta = options.audienceMeta ?? {
name: 'agent',
type: 'device'
}
return await agent.delegate({
return await c.agent.delegate({
...options,
abilities,
audience,
Expand All @@ -194,8 +203,8 @@ export const KeyringProvider: ParentComponent<KeyringProviderProps> = (
}

const addSpace = async (proof: Delegation): Promise<void> => {
const agent = await getAgent()
await agent.importSpaceFromDelegation(proof)
const c = await getClient()
await c.agent.importSpaceFromDelegation(proof)
}

const actions = {
Expand Down
Loading

0 comments on commit ed7409b

Please sign in to comment.