-
Notifications
You must be signed in to change notification settings - Fork 17
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* react wallet * provide more specific interface, type guards from client package * use event targets and listeners
- Loading branch information
1 parent
161ed5c
commit 72d83ea
Showing
22 changed files
with
1,013 additions
and
202 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
'@penumbra-zone/react': major | ||
--- | ||
|
||
initial react wallet |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
'@penumbra-zone/client': minor | ||
--- | ||
|
||
provide type guards for event, and better types for event interface |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
import { | ||
PenumbraNotInstalledError, | ||
PenumbraProviderNotAvailableError, | ||
PenumbraProviderNotConnectedError, | ||
} from './error.js'; | ||
import { PenumbraSymbol } from './symbol.js'; | ||
|
||
export const assertStringIsOrigin = (s?: string) => { | ||
if (!s || new URL(s).origin !== s) { | ||
throw new TypeError('Invalid origin'); | ||
} | ||
return s; | ||
}; | ||
|
||
export const assertGlobalPresent = () => { | ||
if (!window[PenumbraSymbol]) { | ||
throw new PenumbraNotInstalledError(); | ||
} | ||
return window[PenumbraSymbol]; | ||
}; | ||
|
||
/** | ||
* Given a specific origin, identify the relevant injection or throw. An | ||
* `undefined` origin is accepted but will throw. | ||
*/ | ||
export const assertProviderRecord = (providerOrigin?: string) => { | ||
const provider = providerOrigin && assertGlobalPresent()[assertStringIsOrigin(providerOrigin)]; | ||
if (!provider) { | ||
throw new PenumbraProviderNotAvailableError(providerOrigin); | ||
} | ||
return provider; | ||
}; | ||
|
||
export const assertProvider = (providerOrigin?: string) => | ||
assertProviderManifest(providerOrigin).then(() => assertProviderRecord(providerOrigin)); | ||
|
||
/** | ||
* Given a specific origin, identify the relevant injection, and confirm | ||
* provider is connected or throw. An `undefined` origin is accepted but will | ||
* throw. | ||
*/ | ||
export const assertProviderConnected = (providerOrigin?: string) => { | ||
const provider = assertProviderRecord(providerOrigin); | ||
if (!provider.isConnected()) { | ||
throw new PenumbraProviderNotConnectedError(providerOrigin); | ||
} | ||
return provider; | ||
}; | ||
|
||
/** | ||
* Given a specific origin, identify the relevant injection, and confirm its | ||
* manifest is actually present or throw. An `undefined` origin is accepted but | ||
* will throw. | ||
*/ | ||
export const assertProviderManifest = async (providerOrigin?: string, signal?: AbortSignal) => { | ||
// confirm the provider injection is present | ||
const provider = assertProviderRecord(providerOrigin); | ||
|
||
let manifest: unknown; | ||
|
||
try { | ||
// confirm the provider manifest is located at the expected origin | ||
if (new URL(provider.manifest).origin !== providerOrigin) { | ||
throw new Error('Manifest located at unexpected origin'); | ||
} | ||
|
||
// confirm the provider manifest can be fetched, and is json | ||
const req = await fetch(provider.manifest, { signal }); | ||
manifest = await req.json(); | ||
|
||
if (!manifest) { | ||
throw new Error(`Cannot confirm ${providerOrigin} is real.`); | ||
} | ||
} catch (e) { | ||
if (signal?.aborted !== true) { | ||
console.warn(e); | ||
throw new PenumbraProviderNotAvailableError(providerOrigin); | ||
} | ||
} | ||
|
||
return manifest; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,15 +1,51 @@ | ||
import { PenumbraInjectionState, PenumbraSymbol } from './index.js'; | ||
import { PenumbraState } from './state.js'; | ||
import { PenumbraSymbol } from './symbol.js'; | ||
|
||
export class PenumbraInjectionStateEvent extends CustomEvent<{ | ||
export interface PenumbraStateEventDetail { | ||
origin: string; | ||
state?: PenumbraInjectionState; | ||
}> { | ||
constructor(injectionProviderOrigin: string, injectionState?: PenumbraInjectionState) { | ||
state?: PenumbraState; | ||
} | ||
|
||
export class PenumbraStateEvent extends CustomEvent<PenumbraStateEventDetail> { | ||
constructor(penumbraOrigin: string, penumbraState?: PenumbraState) { | ||
super('penumbrastate', { | ||
detail: { | ||
state: injectionState ?? window[PenumbraSymbol]?.[injectionProviderOrigin]?.state(), | ||
origin: injectionProviderOrigin, | ||
origin: penumbraOrigin, | ||
state: penumbraState ?? window[PenumbraSymbol]?.[penumbraOrigin]?.state(), | ||
}, | ||
}); | ||
} | ||
} | ||
|
||
export const isPenumbraStateEvent = (evt: Event): evt is PenumbraStateEvent => | ||
evt instanceof PenumbraStateEvent || ('detail' in evt && isPenumbraStateEventDetail(evt.detail)); | ||
|
||
export const isPenumbraStateEventDetail = (detail: unknown): detail is PenumbraStateEventDetail => | ||
typeof detail === 'object' && | ||
detail !== null && | ||
'origin' in detail && | ||
typeof detail.origin === 'string'; | ||
|
||
// utility type for SpecificEventTarget. any is required for type inference | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
type ParametersTail<T extends (...args: any[]) => any> = | ||
Parameters<T> extends [unknown, ...infer TailParams] ? TailParams : never; | ||
|
||
// like EventTarget, but restricts possible event types | ||
interface SpecificEventTarget<SpecificTypeName extends string, SpecificEvent extends Event = Event> | ||
extends EventTarget { | ||
addEventListener: ( | ||
type: SpecificTypeName, | ||
...rest: ParametersTail<EventTarget['addEventListener']> | ||
) => void; | ||
removeEventListener: ( | ||
type: SpecificTypeName, | ||
...rest: ParametersTail<EventTarget['removeEventListener']> | ||
) => void; | ||
dispatchEvent: (event: SpecificEvent) => boolean; | ||
} | ||
|
||
export type PenumbraStateEventTarget = Omit< | ||
SpecificEventTarget<'penumbrastate', never>, | ||
'dispatchEvent' | ||
>; |
Oops, something went wrong.