Skip to content

Commit

Permalink
Add browser extension support (#504)
Browse files Browse the repository at this point in the history
* Add connect and disconnect handlers

* Add State type and rework state helper function

* Add filesystem event listeners

* Add session events and emitters

* Emit ready post message when Webnative is ready

* Emit session on session create event

We need to update the configuration used by the extension listeners when
a session is created. Adding the session to the event gets us access to
the session.

* Add check to avoid adding listeners more than once

* Add timestamps to messages

* Add capabilities to state

* Move version into a odd object

Version in the app object is confusing because it seems like it might be
the app version, not the ODD version.

* Only send capabilities on state when defined

* Re-work event listener types and interface

* Break up circular dependency

* Rename webnative to odd

* Update changelog

* Move events onto program and rename them

Add event namespaces as strings to event names, for example
`fileSystem:publish`.

* Update version to 0.37.0

* Rename filesystem window message types to fileSystem

* Rename filesystem to fileSystem in State object

* Emit session create from session constructor

* Add event emitter merge
  • Loading branch information
bgins authored Apr 3, 2023
1 parent bd236da commit 2784245
Show file tree
Hide file tree
Showing 12 changed files with 364 additions and 23 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog

### V0.37.0

- Adds browser extension support
- Moves events onto top-level program and renames them. For example, the `local-change` is now `fileSystem:local-change`.
- Adds session create and destroy events

### V0.36.3

Now parses DAG-JSON formatted CIDs.
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "webnative",
"version": "0.36.3",
"version": "0.37.0",
"description": "Webnative SDK",
"keywords": [
"WebCrypto",
Expand Down
2 changes: 1 addition & 1 deletion src/common/version.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export const VERSION = "0.36.3"
export const VERSION = "0.37.0"
export const WASM_WNFS_VERSION = "0.1.7"
7 changes: 6 additions & 1 deletion src/components/auth/implementation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,12 @@ export type Implementation<C> = {
type: string

// `Session` producer
session: (components: C, authenticatedUsername: Maybe<string>, config: Configuration, eventEmitters: { fileSystem: Events.Emitter<Events.FileSystem> }) => Promise<Maybe<Session>>
session: (
components: C,
authenticatedUsername: Maybe<string>,
config: Configuration,
eventEmitters: { fileSystem: Events.Emitter<Events.FileSystem>; session: Events.Emitter<Events.Session<Session>> }
) => Promise<Maybe<Session>>

// Account creation
isUsernameAvailable: (username: string) => Promise<boolean>
Expand Down
9 changes: 7 additions & 2 deletions src/components/auth/implementation/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import * as Reference from "../../reference/implementation.js"
import * as Storage from "../../storage/implementation.js"

import * as Did from "../../../did/index.js"
import * as Events from "../../../events.js"
import * as SessionMod from "../../../session.js"
import * as Ucan from "../../../ucan/index.js"

Expand Down Expand Up @@ -108,16 +109,20 @@ export async function register(
export async function session(
components: Components,
authedUsername: Maybe<string>,
config: Configuration
config: Configuration,
eventEmitters: { session: Events.Emitter<Events.Session<Session>> }
): Promise<Maybe<Session>> {
if (authedUsername) {
return new Session({
const session = new Session({
crypto: components.crypto,
storage: components.storage,
eventEmitter: eventEmitters.session,
type: TYPE,
username: authedUsername
})

return session

} else {
return null

Expand Down
10 changes: 7 additions & 3 deletions src/components/auth/implementation/wnfs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ export async function session(
components: Components,
authedUsername: Maybe<string>,
config: Configuration,
eventEmitters: { fileSystem: Events.Emitter<Events.FileSystem> }
eventEmitters: { fileSystem: Events.Emitter<Events.FileSystem>; session: Events.Emitter<Events.Session<Session>> }
): Promise<Maybe<Session>> {
if (authedUsername) {
// Self-authorize a filesystem UCAN if needed
Expand Down Expand Up @@ -140,14 +140,18 @@ export async function session(
username: authedUsername,
})

// Fin
return new Session({

const session = new Session({
crypto: components.crypto,
fs: fs,
eventEmitter: eventEmitters.session,
storage: components.storage,
type: Base.TYPE,
username: authedUsername
})

// Fin
return session
}

return null
Expand Down
7 changes: 7 additions & 0 deletions src/configuration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,13 @@ export type Configuration = {
* Debugging settings.
*/
debugging?: {
/**
* Should I emit window post messages with session and filesystem information?
*
* @default true
*/
emitWindowPostMessages?: boolean

/**
* Should I add programs to the global context while in debugging mode?
*
Expand Down
36 changes: 32 additions & 4 deletions src/events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,13 @@ export { EventEmitter, EventEmitter as Emitter }
* alternatively you can use `addListener` and `removeListener`.
*
* ```ts
* program.fileSystem.on("local-change", ({ path, root }) => {
* program.on("fileSystem:local-change", ({ path, root }) => {
* console.log("The file system has changed locally 🔔")
* console.log("Changed path:", path)
* console.log("New data root CID:", root)
* })
*
* program.fileSystem.off("publish")
* program.off("fileSystem:publish")
* ```
*/
export type ListenTo<EventMap> = Pick<
Expand All @@ -29,11 +29,20 @@ export type ListenTo<EventMap> = Pick<


export type FileSystem = {
"local-change": { root: CID; path: DistinctivePath<Partitioned<Partition>> }
"publish": { root: CID }
"fileSystem:local-change": { root: CID; path: DistinctivePath<Partitioned<Partition>> }
"fileSystem:publish": { root: CID }
}


export type Session<S> = {
"session:create": { session: S }
"session:destroy": { username: string }
}


export type All<S> = FileSystem & Session<S>


export function createEmitter<EventMap>(): EventEmitter<EventMap> {
return new EventEmitter()
}
Expand All @@ -46,4 +55,23 @@ export function listenTo<EventMap>(emitter: EventEmitter<EventMap>): ListenTo<Ev
on: emitter.on.bind(emitter),
off: emitter.off.bind(emitter),
}
}


export function merge<A, B>(a: EventEmitter<A>, b: EventEmitter<B>): EventEmitter<A & B> {
const merged = createEmitter<A & B>()
const aEmit = a.emit
const bEmit = b.emit

a.emit = <K extends keyof A>(eventName: K, event: (A & B)[ K ]) => {
aEmit.call(a, eventName, event)
merged.emit(eventName, event)
}

b.emit = <K extends keyof B>(eventName: K, event: (A & B)[ K ]) => {
bEmit.call(b, eventName, event)
merged.emit(eventName, event)
}

return merged
}
Loading

0 comments on commit 2784245

Please sign in to comment.