Skip to content

Commit

Permalink
WIP: refactor: thin core
Browse files Browse the repository at this point in the history
  • Loading branch information
hasundue committed Mar 17, 2024
1 parent 8b86963 commit cbd9de6
Show file tree
Hide file tree
Showing 7 changed files with 46 additions and 155 deletions.
34 changes: 6 additions & 28 deletions core/clients.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,61 +5,39 @@ import type {
SubscriptionId,
} from "./protocol.ts";
import { ClientEventTypeRecord, NostrNode, NostrNodeConfig } from "./nodes.ts";
import { NIP } from "@lophus/core/protocol";

//-------------
// Interfaces
//-------------
export interface ClientConfig<N extends NIP = never>
extends NostrNodeConfig<N> {
modules: ClientModule<N, N>[];
}

export type ClientOptions<N extends NIP = never> = Partial<ClientConfig<N>>;
export type ClientConfig = NostrNodeConfig;
export type ClientOptions = Partial<ClientConfig>;

/**
* A class that represents a remote Nostr client.
*/
export class Client<N extends NIP = never> extends NostrNode<
N,
export class Client extends NostrNode<
RelayToClientMessage,
ClientEventTypeRecord
> {
declare ws: WebSocket;
declare config: ClientConfig<N>;
#ready: Promise<void[]>;
declare config: ClientConfig;

/** Writable interface for the subscriptions. */
readonly subscriptions: Map<
SubscriptionId,
WritableStream<NostrEvent>
> = new Map();

constructor(ws: WebSocket, options?: ClientOptions<N>) {
constructor(ws: WebSocket, options?: ClientOptions) {
super(ws, options);
this.config = {
...this.config,
modules: [],
...options,
};
this.ws.addEventListener("message", async (ev: MessageEvent<string>) => {
this.ws.addEventListener("message", (ev: MessageEvent<string>) => {
const message = JSON.parse(ev.data) as ClientToRelayMessage;
// TODO: Validate the message.
await this.#ready;
this.dispatch("message", message);
});
this.#ready = Promise.all(
this.config.modules.map((mod) => mod(this)),
);
}
}

//----------
// Modules
//----------
export interface ClientModule<
P extends NIP = never,
D extends NIP = never,
> {
(client: Client<P | D>): void | Promise<void>;
}
32 changes: 11 additions & 21 deletions core/nodes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import type { PromiseCallbackRecord } from "@lophus/lib/types";
import type {
ClientToRelayMessage,
EventId,
NIP,
NostrEvent,
NostrMessage,
RelayToClientMessage,
Expand All @@ -12,38 +11,32 @@ import type {
} from "./protocol.ts";
import type { WebSocketLike } from "./websockets.ts";

export interface NostrNodeConfig<
N extends NIP = never,
> {
export interface NostrNodeConfig {
nbuffer: number;
nips: N[];
}

export type NostrNodeOptions<
N extends NIP = never,
> = Partial<NostrNodeConfig<N>>;
export type NostrNodeOptions = Partial<NostrNodeConfig>;

/**
* Common base class for relays and clients.
*/
export class NostrNode<
N extends NIP = never,
M extends NostrMessage = NostrMessage,
R extends EventTypeRecord = EventTypeRecord,
> extends EventTarget {
readonly writable: WritableStream<M>;
readonly config: Readonly<NostrNodeConfig<N>>;
readonly config: Readonly<NostrNodeConfig>;

constructor(
readonly ws: WebSocketLike,
opts: NostrNodeOptions<N> = {},
opts: NostrNodeOptions = {},
) {
super();
this.writable = new WritableStream({
write: (msg) => this.ws.send(JSON.stringify(msg)),
close: () => this.ws.close(),
});
this.config = { nbuffer: 10, nips: [], ...opts };
this.config = { nbuffer: 10, ...opts };
}

send(msg: M): void | Promise<void> {
Expand All @@ -67,15 +60,15 @@ export class NostrNode<
declare addEventListener: <T extends EventType<R>>(
type: T,
listener:
| NostrNodeEventListenerOrEventListenerObject<N, M, R, T>
| NostrNodeEventListenerOrEventListenerObject<M, R, T>
| null,
options?: AddEventListenerOptions,
) => void;

declare removeEventListener: <T extends EventType<R>>(
type: T,
listener:
| NostrNodeEventListenerOrEventListenerObject<N, M, R, T>
| NostrNodeEventListenerOrEventListenerObject<M, R, T>
| null,
options?: boolean | EventListenerOptions,
) => void;
Expand Down Expand Up @@ -161,28 +154,25 @@ export class NostrNodeEvent<
}

type NostrNodeEventListenerOrEventListenerObject<
N extends NIP,
M extends NostrMessage,
R extends EventTypeRecord,
T extends EventType<R>,
> =
| NostrNodeEventListener<N, M, R, T>
| NostrNodeEventListenerObject<N, M, R, T>;
| NostrNodeEventListener<M, R, T>
| NostrNodeEventListenerObject<M, R, T>;

type NostrNodeEventListener<
N extends NIP,
W extends NostrMessage,
R extends EventTypeRecord,
T extends EventType<R>,
> // deno-lint-ignore no-explicit-any
= (this: NostrNode<N, W, R>, ev: MessageEvent<R[T]>) => any;
= (this: NostrNode<W, R>, ev: MessageEvent<R[T]>) => any;

type NostrNodeEventListenerObject<
N extends NIP,
W extends NostrMessage,
R extends EventTypeRecord,
T extends EventType<R>,
> = {
// deno-lint-ignore no-explicit-any
handleEvent(this: NostrNode<N, W, R>, ev: MessageEvent<R[T]>): any;
handleEvent(this: NostrNode<W, R>, ev: MessageEvent<R[T]>): any;
};
17 changes: 0 additions & 17 deletions core/nodes_test.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,9 @@
import { assertEquals } from "@std/assert";
import { describe, it } from "@std/testing/bdd";
import { assertType, Has } from "@std/testing/types";
import { MockWebSocket } from "@lophus/lib/testing";
import "@lophus/nips";
import { NIP } from "./protocol.ts";
import { NostrNode } from "./nodes.ts";

type HasNIP<T, N> = T extends NostrNode<infer M> ? Has<M, N> : false;

describe("NostrNode", () => {
describe("constructor", () => {
it("should not have any NIPs configured by default", () => {
const node = new NostrNode(new MockWebSocket());
assertType<HasNIP<typeof node, NIP>>(false);
});

it("should inherit the type of `nips`", () => {
const node = new NostrNode(new MockWebSocket(), { nips: [1] });
assertType<HasNIP<typeof node, 1>>(true);
});
});

describe("writable", () => {
const node = new NostrNode(new MockWebSocket());
let writer: WritableStreamDefaultWriter;
Expand Down
53 changes: 13 additions & 40 deletions core/protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,24 +10,6 @@

import type { AlphabetLetter, Brand, Stringified } from "@lophus/lib/types";

// ----------------------
// Extendable interfaces
// ----------------------

export interface NipRecord {}

export interface EventKindRecord {}

export interface TagRecord {}

export interface ClientToRelayMessageRecord<
K extends EventKind = EventKind,
> {}

export interface RelayToClientMessageRecord<
K extends EventKind = EventKind,
> {}

// ----------------------
// Events and signatures
// ----------------------
Expand Down Expand Up @@ -75,6 +57,8 @@ export interface EventInit<K extends EventKind = EventKind> {
// Tags
// ----------------------

export interface TagRecord {}

export type TagType = keyof TagRecord & string;
export type TagParam = string | undefined;

Expand Down Expand Up @@ -105,7 +89,9 @@ export type OptionalTag<K extends EventKind> = EventKindRecord[K] extends
export type RelayUrl = `wss://${string}` | `ws://${string}`;
export type SubscriptionId = Brand<string, "SubscriptionId">;

export type NostrMessage = ClientToRelayMessage | RelayToClientMessage;
export interface ClientToRelayMessageRecord<
K extends EventKind = EventKind,
> {}

export type ClientToRelayMessage<
T extends ClientToRelayMessageType = ClientToRelayMessageType,
Expand All @@ -115,6 +101,10 @@ export type ClientToRelayMessage<
}[T];
export type ClientToRelayMessageType = keyof ClientToRelayMessageRecord;

export interface RelayToClientMessageRecord<
K extends EventKind = EventKind,
> {}

export type RelayToClientMessage<
T extends RelayToClientMessageType = RelayToClientMessageType,
K extends EventKind = EventKind,
Expand Down Expand Up @@ -152,10 +142,14 @@ export type SubscriptionFilter<
limit?: number;
};

export type NostrMessage = ClientToRelayMessage | RelayToClientMessage;

// ----------------------
// Events
// ----------------------

export interface EventKindRecord {}

export type EventKind = keyof EventKindRecord & number;

export interface EventKindRecordEntry {
Expand All @@ -173,24 +167,3 @@ export type EventContent<K extends EventKind> = EventKindRecord[K] extends
export type ResponsePrefix<K extends EventKind = EventKind> =
EventKindRecord[K] extends { ResponsePrefix: infer P extends string } ? P
: DefaultResponsePrefix;

export type RegularEventKind = Brand<EventKind, "Regular">;
export type ReplaceableEventKind = Brand<EventKind, "Replaceable">;
export type EphemeralEventKind = Brand<EventKind, "Ephemeral">;
export type ParameterizedReplaceableEventKind = Brand<
EventKind,
"ParameterizedReplaceable"
>;

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

export type NIP = keyof NipRecord & number;

export interface NipRecordEntry {
ClientToRelayMessage: ClientToRelayMessageType;
RelayToClientMessage: RelayToClientMessageType;
EventKind: EventKind;
Tag: TagType;
}
Loading

0 comments on commit cbd9de6

Please sign in to comment.