Skip to content

Commit

Permalink
refactor: simplify modular architecture (#27)
Browse files Browse the repository at this point in the history
  • Loading branch information
hasundue authored Feb 15, 2024
1 parent 2e63a5a commit 45c93ed
Show file tree
Hide file tree
Showing 9 changed files with 314 additions and 419 deletions.
99 changes: 35 additions & 64 deletions core/clients.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import type {
ClientToRelayMessage,
ClientToRelayMessageType,
NostrEvent,
RelayToClientMessage,
SubscriptionFilter,
SubscriptionId,
} from "./protocol.d.ts";
import {
Expand All @@ -12,21 +10,35 @@ import {
NostrNodeEvent,
NostrNodeModule,
} from "./nodes.ts";
import { NIPs } from "./nips.ts";
import { importNips } from "./nips.ts";

// ----------------------
// NIPs
// ----------------------

const nips = await NIPs.import<ClientModule>(import.meta.url, "../nips");
const NIPs = await importNips<
RelayToClientMessage,
ClientEventTypeRecord,
Client
>(import.meta.url, "../nips");

// ----------------------
// Interfaces
// ----------------------

export type ClientConfig = NostrNodeConfig<
RelayToClientMessage,
ClientEventTypeRecord
>;

export type ClientOptions = Partial<ClientConfig>;

/**
* A class that represents a remote Nostr client.
*/
export class Client extends NostrNode<
RelayToClientMessage,
EventDataTypeRecord,
ClientFunctionParameterTypeRecord
ClientEventTypeRecord
> {
declare ws: WebSocket;

Expand All @@ -41,77 +53,36 @@ export class Client extends NostrNode<
constructor(ws: WebSocket, opts?: ClientOptions) {
super(ws, {
...opts,
modules: nips.concat(opts?.modules ?? []),
modules: NIPs.concat(opts?.modules ?? []),
});
this.ws.addEventListener("message", (ev: MessageEvent<string>) => {
// TODO: Validate the type of the message.
const message = JSON.parse(ev.data) as ClientToRelayMessage;
this.callFunction("handleClientToRelayMessage", {
message,
client: this,
});
// TODO: Validate the message.
this.dispatchEvent(new ClientEvent("message", message));
});
}
}

type ClientConfig = NostrNodeConfig<ClientFunctionParameterTypeRecord>;
export type ClientOptions = Partial<ClientConfig>;

// ------------------------------
// Functions
// Events
// ------------------------------

export type ClientModule = NostrNodeModule<ClientFunctionParameterTypeRecord>;

type ClientFunctionParameterTypeRecord = {
[K in keyof _FunctionParameterTypeRecord]:
& _FunctionParameterTypeRecord[K]
& ClientFunctionContext;
};

type _FunctionParameterTypeRecord = {
"handleClientToRelayMessage": {
message: ClientToRelayMessage;
};
"handleSubscriptionMessage": {
message: SubscriptionMessage;
controller: ReadableStreamDefaultController<NostrEvent>;
} & SubscriptionContext;
"acceptEvent": {
event: NostrEvent;
};
};

interface ClientFunctionContext {
client: Client;
export interface ClientEventTypeRecord {
"message": ClientToRelayMessage;
}

interface SubscriptionContext {
id: SubscriptionId;
filters: SubscriptionFilter[];
}
export type ClientEventType = keyof ClientEventTypeRecord;

export class ClientEvent<
T extends ClientEventType = ClientEventType,
> extends NostrNodeEvent<ClientEventTypeRecord, T> {}

// ------------------------------
// Events
// Modules
// ------------------------------

type EventDataTypeRecord = {
[T in SubscriptionId]: SubscriptionMessage;
};

type SubscriptionMessage = {
[T in ClientToRelayMessageType]: ClientToRelayMessage<T>[1] extends
SubscriptionId ? ClientToRelayMessage<T> : never;
}[ClientToRelayMessageType];

export class ClientSubscriptionEvent extends NostrNodeEvent<
EventDataTypeRecord,
SubscriptionId
> {
constructor(
type: SubscriptionId,
init: MessageEventInit<SubscriptionMessage>,
) {
super(type, init);
}
}
export type ClientModule = NostrNodeModule<
RelayToClientMessage,
ClientEventTypeRecord,
Client
>;
59 changes: 30 additions & 29 deletions core/nips.ts
Original file line number Diff line number Diff line change
@@ -1,33 +1,34 @@
import type { NIP } from "./protocol.d.ts";
import { NostrNodeModule } from "./nodes.ts";
import type { NIP, NostrMessage } from "./protocol.d.ts";
import { EventTypeRecord, NostrNode, NostrNodeModule } from "./nodes.ts";

export const NIPs = {
/**
* Import a Nostr module from a URL.
*
* @param meta - The path to the module to which the NIPs are attached (mostly import.meta.url).
* @param root - The path to the root of NIP module to import.
*/
// deno-lint-ignore no-explicit-any
import<M extends NostrNodeModule<any>>(
meta: string,
root: string,
) {
const url = new URL(meta);
const base = url.pathname.split("/").slice(-1)[0];
return Promise.all(
url.searchParams.get("nips")?.split(",").map(Number).map(
(nip) =>
import(
new URL(
`${root}/${nipToString(nip)}/${base}`,
import.meta.url,
).href
) as Promise<M>,
) ?? [],
);
},
};
/**
* Import a NostrNode module from a URL.
*
* @param meta - The path to the module to which the NIPs are attached (mostly import.meta.url).
* @param root - The path to the root of NIP module to import.
*/
export function importNips<
W extends NostrMessage = NostrMessage,
R extends EventTypeRecord = EventTypeRecord,
N extends NostrNode<W, R> = NostrNode<W, R>,
>(
meta: string,
root: string,
) {
const url = new URL(meta);
const base = url.pathname.split("/").slice(-1)[0];
return Promise.all(
url.searchParams.get("nips")?.split(",").map(Number).map(
(nip) =>
import(
new URL(
`${root}/${nipToString(nip)}/${base}`,
import.meta.url,
).href
) as Promise<NostrNodeModule<W, R, N>>,
) ?? [],
);
}

/**
* Convert a NIP to a string. If the NIP is less than 10, a leading zero is
Expand Down
Loading

0 comments on commit 45c93ed

Please sign in to comment.