From 3301616e7d219abe3e9ccdda0f6db469a0153830 Mon Sep 17 00:00:00 2001 From: Jon Staab Date: Tue, 8 Apr 2025 15:08:00 -0700 Subject: [PATCH] Bring back typedoc --- .gitignore | 8 +- package.json | 3 + packages/app/README.md | 2 +- packages/app/__tests__/collection.test.ts | 2 +- packages/app/__tests__/thunk.test.ts | 4 +- packages/content/__tests__/content.test.ts | 80 ++++++------- packages/content/__tests__/truncate.test.ts | 6 +- packages/feeds/__tests__/compiler.test.ts | 10 +- packages/net/__tests__/Tracker.test.ts | 2 +- packages/net/__tests__/adapter.test.ts | 18 +-- packages/net/__tests__/auth.test.ts | 6 +- packages/net/__tests__/pool.test.ts | 8 +- packages/net/__tests__/publish.test.ts | 8 +- packages/net/__tests__/socket.test.ts | 23 ++-- packages/relay/__tests__/repository.test.ts | 2 +- packages/store/__tests__/index.test.ts | 3 +- packages/util/__tests__/Filters.test.ts | 83 ++++++++------ pnpm-lock.yaml | 118 ++++++++++++++++++-- typedoc.json | 10 ++ 19 files changed, 261 insertions(+), 135 deletions(-) create mode 100644 typedoc.json diff --git a/.gitignore b/.gitignore index 781ac9c..be40c42 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,9 @@ node_modules **/dist -**/cached -**/cache -**/temp -**/reference +docs/.vitepress/dist +docs/.vitepress/cache +docs/.vitepress/.temp +docs/reference results .nyc_output *.tsbuildinfo diff --git a/package.json b/package.json index e4d3d16..2ec4e78 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,9 @@ "globals": "~16.0.0", "happy-dom": "^17.4.4", "prettier": "~3.5.3", + "typedoc": "^0.28.2", + "typedoc-plugin-markdown": "^4.6.1", + "typedoc-vitepress-theme": "^1.1.2", "typescript": "~5.8.0", "typescript-eslint": "~8.29.0", "vitepress": "^1.6.3", diff --git a/packages/app/README.md b/packages/app/README.md index 3abb2f6..be366fb 100644 --- a/packages/app/README.md +++ b/packages/app/README.md @@ -1,4 +1,4 @@ -# @welshman/store [![version](https://badgen.net/npm/v/@welshman/store)](https://npmjs.com/package/@welshman/store) +# @welshman/app [![version](https://badgen.net/npm/v/@welshman/app)](https://npmjs.com/package/@welshman/app) Utilities for dealing with svelte stores when using welshman. diff --git a/packages/app/__tests__/collection.test.ts b/packages/app/__tests__/collection.test.ts index e4370e0..abb5262 100644 --- a/packages/app/__tests__/collection.test.ts +++ b/packages/app/__tests__/collection.test.ts @@ -179,7 +179,7 @@ describe("collection", () => { getKey: item => item.id, }) - const derived = col.deriveItem(null) + const derived = col.deriveItem(undefined) expect(get(derived)).toBeUndefined() }) diff --git a/packages/app/__tests__/thunk.test.ts b/packages/app/__tests__/thunk.test.ts index 0167d8b..9f1264a 100644 --- a/packages/app/__tests__/thunk.test.ts +++ b/packages/app/__tests__/thunk.test.ts @@ -1,5 +1,5 @@ import {now} from "@welshman/lib" -import {publish, PublishStatus, MockAdapter} from "@welshman/net" +import {PublishStatus, MockAdapter} from "@welshman/net" import {NOTE, makeEvent} from "@welshman/util" import {Nip01Signer} from "@welshman/signer" import {LOCAL_RELAY_URL} from "@welshman/relay" @@ -112,7 +112,7 @@ describe("thunk", () => { const send = vi.fn() const track = vi.spyOn(tracker, 'track') const thunk = makeThunk(mockRequest) - let status = {} + let status: Record = {} // Subscribe to status updates thunk.status.subscribe(_status => { diff --git a/packages/content/__tests__/content.test.ts b/packages/content/__tests__/content.test.ts index 102601d..a457067 100644 --- a/packages/content/__tests__/content.test.ts +++ b/packages/content/__tests__/content.test.ts @@ -1,5 +1,5 @@ import {describe, it, expect} from "vitest" -import * as Content from "../src" +import {ParsedLink, ParsedType, parse, reduceLinks, renderAsHtml, renderAsText} from "../src" import {npubEncode, noteEncode} from "nostr-tools/nip19" describe("Content Parsing", () => { @@ -7,51 +7,55 @@ describe("Content Parsing", () => { const nevent = noteEncode("ff".repeat(32)) describe("Basic Parsing", () => { it("should parse plain text", () => { - const result = Content.parse({content: "Hello world"}) + const result = parse({content: "Hello world"}) expect(result).toEqual([ - {type: Content.ParsedType.Text, value: "Hello world", raw: "Hello world"}, + {type: ParsedType.Text, value: "Hello world", raw: "Hello world"}, ]) }) it("should parse newlines", () => { - const result = Content.parse({content: "Hello\nworld"}) + const result = parse({content: "Hello\nworld"}) expect(result).toEqual([ - {type: Content.ParsedType.Text, value: "Hello", raw: "Hello"}, - {type: Content.ParsedType.Newline, value: "\n", raw: "\n"}, - {type: Content.ParsedType.Text, value: "world", raw: "world"}, + {type: ParsedType.Text, value: "Hello", raw: "Hello"}, + {type: ParsedType.Newline, value: "\n", raw: "\n"}, + {type: ParsedType.Text, value: "world", raw: "world"}, ]) }) }) describe("Link Parsing", () => { it("should parse basic URLs", () => { - const result = Content.parse({content: "Check https://example.com"}) - expect(result[1]).toMatchObject({ - type: Content.ParsedType.Link, + const result = parse({content: "Check https://example.com"}) + const parsed = result[1] as ParsedLink + + expect(parsed).toMatchObject({ + type: ParsedType.Link, value: { url: expect.any(URL), - // isMedia: false, }, }) - expect(result[1].value.url.toString()).toBe("https://example.com/") + + expect(parsed.value.url.toString()).toBe("https://example.com/") }) it("should parse URLs without protocol", () => { - const result = Content.parse({content: "Visit example.com"}) - expect(result[1]).toMatchObject({ - type: Content.ParsedType.Link, + const result = parse({content: "Visit example.com"}) + const parsed = result[1] as ParsedLink + + expect(parsed).toMatchObject({ + type: ParsedType.Link, value: { url: expect.any(URL), - // isMedia: false, }, }) - expect(result[1].value.url.toString()).toBe("https://example.com/") + + expect(parsed.value.url.toString()).toBe("https://example.com/") }) it("should identify media links", () => { - const result = Content.parse({content: "https://example.com/image.jpg"}) + const result = parse({content: "https://example.com/image.jpg"}) expect(result[0]).toMatchObject({ - type: Content.ParsedType.Link, + type: ParsedType.Link, value: { url: expect.any(URL), meta: {}, @@ -62,46 +66,46 @@ describe("Content Parsing", () => { describe("Nostr Entity Parsing", () => { it("should parse nostr profiles", () => { - const result = Content.parse({ + const result = parse({ content: `nostr:${npub}`, }) expect(result[0]).toMatchObject({ - type: Content.ParsedType.Profile, + type: ParsedType.Profile, }) }) it("should parse nostr events", () => { - const result = Content.parse({ + const result = parse({ content: `nostr:${nevent}`, }) expect(result[0]).toMatchObject({ - type: Content.ParsedType.Event, + type: ParsedType.Event, }) }) }) describe("Special Content Parsing", () => { it("should parse code blocks", () => { - const result = Content.parse({content: "```const x = 1```"}) + const result = parse({content: "```const x = 1```"}) expect(result[0]).toMatchObject({ - type: Content.ParsedType.Code, + type: ParsedType.Code, value: "const x = 1", }) }) it("should parse inline code", () => { - const result = Content.parse({content: "Use `npm install`"}) + const result = parse({content: "Use `npm install`"}) expect(result[1]).toMatchObject({ - type: Content.ParsedType.Code, + type: ParsedType.Code, value: "npm install", }) }) it("should parse topics", () => { - const result = Content.parse({content: "#nostr is cool"}) + const result = parse({content: "#nostr is cool"}) expect(result[0]).toMatchObject({ - type: Content.ParsedType.Topic, + type: ParsedType.Topic, value: "nostr", }) }) @@ -109,27 +113,27 @@ describe("Content Parsing", () => { describe("Rendering", () => { it("should render as text", () => { - const parsed = Content.parse({content: "Hello https://example.com"}) - const rendered = Content.renderAsText(parsed).toString() + const parsed = parse({content: "Hello https://example.com"}) + const rendered = renderAsText(parsed).toString() expect(rendered).toContain("Hello") expect(rendered).toContain("https://example.com") }) it("should render as HTML", () => { - const parsed = Content.parse({content: "Hello https://example.com"}) - const rendered = Content.renderAsHtml(parsed).toString() + const parsed = parse({content: "Hello https://example.com"}) + const rendered = renderAsHtml(parsed).toString() expect(rendered).toContain(' { it("should reduce consecutive image links into a grid", () => { - const content = Content.parse({ + const content = parse({ content: "https://example.com/1.jpg\nhttps://example.com/2.jpg https://example.com/2.jpg", }) - const reduced = Content.reduceLinks(content) + const reduced = reduceLinks(content) expect(reduced[0]).toMatchObject({ - type: Content.ParsedType.LinkGrid, + type: ParsedType.LinkGrid, value: { links: expect.any(Array), }, @@ -139,12 +143,12 @@ describe("Content Parsing", () => { describe("Legacy Mention Parsing", () => { it("should parse legacy mentions", () => { - const result = Content.parse({ + const result = parse({ content: "#[0]", tags: [["p", "1234567890"]], }) expect(result[0]).toMatchObject({ - type: Content.ParsedType.Profile, + type: ParsedType.Profile, }) }) }) diff --git a/packages/content/__tests__/truncate.test.ts b/packages/content/__tests__/truncate.test.ts index ef7a20b..0db2fa0 100644 --- a/packages/content/__tests__/truncate.test.ts +++ b/packages/content/__tests__/truncate.test.ts @@ -36,7 +36,6 @@ describe("Content Truncation", () => { value: { url: new URL("https://example.com/image.jpg"), meta: {}, - isMedia: true, }, raw: "https://example.com/image.jpg", }, @@ -83,7 +82,6 @@ describe("Content Truncation", () => { value: { url: new URL("https://example.com/image.jpg"), meta: {}, - isMedia: true, }, raw: "https://example.com/image.jpg", }, @@ -141,8 +139,8 @@ describe("Content Truncation", () => { type: ParsedType.LinkGrid, value: { links: [ - {url: new URL("https://example.com/1.jpg"), meta: {}, isMedia: true}, - {url: new URL("https://example.com/2.jpg"), meta: {}, isMedia: true}, + {url: new URL("https://example.com/1.jpg"), meta: {}}, + {url: new URL("https://example.com/2.jpg"), meta: {}}, ], }, raw: "", diff --git a/packages/feeds/__tests__/compiler.test.ts b/packages/feeds/__tests__/compiler.test.ts index fa42574..9fe52b7 100644 --- a/packages/feeds/__tests__/compiler.test.ts +++ b/packages/feeds/__tests__/compiler.test.ts @@ -1,6 +1,6 @@ import {defaultTagFeedMappings} from "@welshman/feeds" import {now} from "@welshman/lib" -import {getAddress, type TrustedEvent} from "@welshman/util" +import {getAddress, TrustedEvent} from "@welshman/util" import {beforeEach, describe, expect, it, vi} from "vitest" import {FeedCompiler} from "../src/compiler" import {Feed, FeedType, Scope} from "../src/core" @@ -269,7 +269,7 @@ describe("FeedCompiler", () => { sig: "sig1", } - mockOptions.requestDVM.mockImplementation(async ({onEvent}) => { + mockOptions.requestDVM.mockImplementation(async ({onEvent}: any) => { await onEvent(mockEvent) }) @@ -307,7 +307,7 @@ describe("FeedCompiler", () => { sig: "sig1", } - mockOptions.request.mockImplementation(({onEvent}) => { + mockOptions.request.mockImplementation(({onEvent}: any) => { onEvent(mockEvent) }) @@ -345,7 +345,7 @@ describe("FeedCompiler", () => { sig: "sig1", } - mockOptions.request.mockImplementation(({onEvent}) => { + mockOptions.request.mockImplementation(({onEvent}: any) => { onEvent(labelEvent) }) @@ -381,7 +381,7 @@ describe("FeedCompiler", () => { sig: "sig1", } - mockOptions.requestDVM.mockImplementation(async ({onEvent}) => { + mockOptions.requestDVM.mockImplementation(async ({onEvent}: any) => { await onEvent(mockEvent) }) diff --git a/packages/net/__tests__/Tracker.test.ts b/packages/net/__tests__/Tracker.test.ts index 558518d..8f7cc49 100644 --- a/packages/net/__tests__/Tracker.test.ts +++ b/packages/net/__tests__/Tracker.test.ts @@ -1,5 +1,5 @@ -import {Tracker} from "../src/Tracker" import {vi, describe, it, expect, beforeEach} from "vitest" +import {Tracker} from "../src/tracker" describe("Tracker", () => { let tracker: Tracker diff --git a/packages/net/__tests__/adapter.test.ts b/packages/net/__tests__/adapter.test.ts index f4ff874..dc37e78 100644 --- a/packages/net/__tests__/adapter.test.ts +++ b/packages/net/__tests__/adapter.test.ts @@ -1,20 +1,20 @@ import EventEmitter from "events" import { describe, expect, it, vi, beforeEach, afterEach } from "vitest" import { isRelayUrl } from "@welshman/util" -import { LocalRelay, LOCAL_RELAY_URL } from "@welshman/relay" +import { LocalRelay, Repository, LOCAL_RELAY_URL } from "@welshman/relay" import { AdapterEvent, SocketAdapter, LocalAdapter, getAdapter } from "../src/adapter" import { ClientMessage, RelayMessage } from "../src/message" import { Socket, SocketEvent } from "../src/socket" import { Pool } from "../src/pool" vi.mock('isomorphic-ws', () => { - const WebSocket = vi.fn(function () { + const WebSocket = vi.fn(function (this: any) { setTimeout(() => this.onopen()) }) WebSocket.prototype.send = vi.fn() - WebSocket.prototype.close = vi.fn(function () { + WebSocket.prototype.close = vi.fn(function (this: any) { this.onclose() }) @@ -120,12 +120,14 @@ describe("LocalAdapter", () => { describe("getAdapter", () => { let pool: Pool - let relay: LocalRelay + let repository: Repository beforeEach(() => { - pool = new Pool() - relay = new LocalRelay() - pool.get = vi.fn().mockReturnValue(new Socket("wss://test.relay")) + pool = new Pool({ + makeSocket: () => new Socket("wss://test.relay"), + }) + + repository = new Repository() }) afterEach(() => { @@ -134,7 +136,7 @@ describe("getAdapter", () => { it("should return LocalAdapter for local relay URL", () => { const url = LOCAL_RELAY_URL - const adapter = getAdapter(url, { relay }) + const adapter = getAdapter(url, { repository }) expect(adapter).toBeInstanceOf(LocalAdapter) }) diff --git a/packages/net/__tests__/auth.test.ts b/packages/net/__tests__/auth.test.ts index 00570d9..c5745af 100644 --- a/packages/net/__tests__/auth.test.ts +++ b/packages/net/__tests__/auth.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it, vi, beforeEach, afterEach } from "vitest" import { Socket, SocketStatus, SocketEvent } from "../src/socket" -import { makeEvent, CLIENT_AUTH } from "@welshman/util" +import { makeEvent, StampedEvent, CLIENT_AUTH } from "@welshman/util" import { Nip01Signer } from "@welshman/signer" import { AuthState, AuthStatus, AuthStateEvent, makeAuthEvent } from "../src/auth" import EventEmitter from "events" @@ -146,7 +146,7 @@ describe('auth', () => { socket.auth.challenge = "challenge123" socket.auth.status = AuthStatus.Requested - const sign = async e => { + const sign = async (e: StampedEvent) => { event = await Nip01Signer.ephemeral().sign(e) return event @@ -154,7 +154,7 @@ describe('auth', () => { await socket.auth.authenticate(sign) - expect(socket.auth.request).toStrictEqual(event.id) + expect(socket.auth.request).toStrictEqual(event!.id) expect(sendSpy).toHaveBeenCalledWith(["AUTH", event]) }) }) diff --git a/packages/net/__tests__/pool.test.ts b/packages/net/__tests__/pool.test.ts index 3683363..7c2793e 100644 --- a/packages/net/__tests__/pool.test.ts +++ b/packages/net/__tests__/pool.test.ts @@ -4,13 +4,13 @@ import { Pool, makeSocket } from "../src/pool" import { normalizeRelayUrl } from "@welshman/util" vi.mock('isomorphic-ws', () => { - const WebSocket = vi.fn(function () { + const WebSocket = vi.fn(function (this: any) { setTimeout(() => this.onopen()) }) WebSocket.prototype.send = vi.fn() - WebSocket.prototype.close = vi.fn(function () { + WebSocket.prototype.close = vi.fn(function (this: any) { this.onclose() }) @@ -97,7 +97,7 @@ describe("Pool", () => { it("should remove and cleanup existing socket", () => { const mockSocket = { url: "wss://test.relay", cleanup: vi.fn() } - pool._data.set(mockSocket.url, mockSocket) + pool._data.set(mockSocket.url, mockSocket as unknown as Socket) pool.remove(mockSocket.url) expect(mockSocket.cleanup).toHaveBeenCalled() @@ -116,7 +116,7 @@ describe("Pool", () => { const mockSockets = urls.map(url => ({ url, cleanup: vi.fn() })) for (const mockSocket of mockSockets) { - pool._data.set(mockSocket.url, mockSocket) + pool._data.set(mockSocket.url, mockSocket as unknown as Socket) } pool.clear() diff --git a/packages/net/__tests__/publish.test.ts b/packages/net/__tests__/publish.test.ts index 2f83c04..5a67ca8 100644 --- a/packages/net/__tests__/publish.test.ts +++ b/packages/net/__tests__/publish.test.ts @@ -103,14 +103,14 @@ describe("SinglePublish", () => { pub.on(PublishEvent.Complete, completeSpy) pub.on(PublishEvent.Timeout, timeoutSpy) - await vi.runAllTimers(200) + await vi.runAllTimers() expect(sendSpy).toHaveBeenCalledWith([ClientMessageType.Event, event]) await vi.runAllTimers() expect(successSpy).not.toHaveBeenCalled() - expect(failureSpy).not.toHaveBeenCalled(event.id, "hi") + expect(failureSpy).not.toHaveBeenCalled() expect(completeSpy).toHaveBeenCalled() expect(timeoutSpy).toHaveBeenCalled() }) @@ -137,7 +137,7 @@ describe("SinglePublish", () => { pub.on(PublishEvent.Complete, completeSpy) pub.on(PublishEvent.Timeout, abortSpy) - await vi.runAllTimers(200) + await vi.runAllTimers() expect(sendSpy).toHaveBeenCalledWith([ClientMessageType.Event, event]) @@ -146,7 +146,7 @@ describe("SinglePublish", () => { await vi.runAllTimers() expect(successSpy).not.toHaveBeenCalled() - expect(failureSpy).not.toHaveBeenCalled(event.id, "hi") + expect(failureSpy).not.toHaveBeenCalled() expect(completeSpy).toHaveBeenCalled() expect(abortSpy).toHaveBeenCalled() }) diff --git a/packages/net/__tests__/socket.test.ts b/packages/net/__tests__/socket.test.ts index 5cfd718..97ebaa3 100644 --- a/packages/net/__tests__/socket.test.ts +++ b/packages/net/__tests__/socket.test.ts @@ -5,13 +5,13 @@ import { Socket, SocketStatus, SocketEvent } from "../src/socket" import { ClientMessage, RelayMessage } from "../src/message" vi.mock('isomorphic-ws', () => { - const WebSocket = vi.fn(function () { + const WebSocket = vi.fn(function (this: any) { setTimeout(() => this.onopen()) }) WebSocket.prototype.send = vi.fn() - WebSocket.prototype.close = vi.fn(function () { + WebSocket.prototype.close = vi.fn(function (this: any) { this.onclose() }) @@ -77,11 +77,9 @@ describe("Socket", () => { socket.open() - const ws = socket._ws - socket.close() - expect(ws.close).toHaveBeenCalled() + expect(socket._ws!.close).toHaveBeenCalled() expect(statusSpy).toHaveBeenCalledWith(SocketStatus.Closed, "wss://test.relay") }) }) @@ -102,14 +100,14 @@ describe("Socket", () => { socket.on(SocketEvent.Send, sendSpy) socket.open() - socket._ws.onopen() + socket._ws?.onopen?.(undefined as unknown as any) const message: ClientMessage = ["EVENT", { id: "123", kind: 1 }] socket.send(message) await vi.runAllTimers() - expect(socket._ws.send).toHaveBeenCalledWith(JSON.stringify(message)) + expect(socket._ws!.send).toHaveBeenCalledWith(JSON.stringify(message)) expect(sendSpy).toHaveBeenCalledWith(message, "wss://test.relay") }) }) @@ -121,9 +119,8 @@ describe("Socket", () => { socket.open() const message: RelayMessage = ["EVENT", "123", { id: "123", kind: 1 }] - socket._ws.onmessage({ data: JSON.stringify(message) }) + socket._ws?.onmessage?.({data: JSON.stringify(message)} as unknown as any) - // Allow task queue to process await vi.runAllTimers() expect(receiveSpy).toHaveBeenCalledWith(message, "wss://test.relay") @@ -134,7 +131,7 @@ describe("Socket", () => { socket.on(SocketEvent.Error, errorSpy) socket.open() - socket._ws.onmessage({ data: "invalid json" }) + socket._ws?.onmessage?.({data: "invalid json"} as unknown as any) expect(errorSpy).toHaveBeenCalledWith("Invalid message received", "wss://test.relay") }) @@ -144,7 +141,7 @@ describe("Socket", () => { socket.on(SocketEvent.Error, errorSpy) socket.open() - socket._ws.onmessage({ data: JSON.stringify({ not: "an array" }) }) + socket._ws?.onmessage?.({data: JSON.stringify({not: "an array"})} as unknown as any) expect(errorSpy).toHaveBeenCalledWith("Invalid message received", "wss://test.relay") }) @@ -158,7 +155,7 @@ describe("Socket", () => { socket.cleanup() - expect(ws.close).toHaveBeenCalled() + expect(ws!.close).toHaveBeenCalled() expect(socket.listenerCount(SocketEvent.Send)).toBe(0) }) }) @@ -169,7 +166,7 @@ describe("Socket", () => { socket.on(SocketEvent.Status, statusSpy) socket.open() - socket._ws.onerror() + socket._ws?.onerror?.(undefined as unknown as any) expect(statusSpy).toHaveBeenCalledWith(SocketStatus.Error, "wss://test.relay") }) diff --git a/packages/relay/__tests__/repository.test.ts b/packages/relay/__tests__/repository.test.ts index e7a9869..9307427 100644 --- a/packages/relay/__tests__/repository.test.ts +++ b/packages/relay/__tests__/repository.test.ts @@ -1,7 +1,7 @@ import {describe, it, vi, expect, beforeEach} from "vitest" import {now} from "@welshman/lib" import {getAddress, TrustedEvent, DELETE, MUTES} from "@welshman/util" -import {Repository} from "../src/Repository" +import {Repository} from "../src/repository" describe("Repository", () => { beforeEach(() => { diff --git a/packages/store/__tests__/index.test.ts b/packages/store/__tests__/index.test.ts index 5653007..a63c820 100644 --- a/packages/store/__tests__/index.test.ts +++ b/packages/store/__tests__/index.test.ts @@ -1,4 +1,5 @@ -import type {Repository, TrustedEvent} from "@welshman/util" +import {TrustedEvent} from "@welshman/util" +import {Repository} from "@welshman/relay" import {get} from "svelte/store" import {afterEach, beforeEach, describe, expect, it, vi} from "vitest" import { diff --git a/packages/util/__tests__/Filters.test.ts b/packages/util/__tests__/Filters.test.ts index 35341c6..08b655c 100644 --- a/packages/util/__tests__/Filters.test.ts +++ b/packages/util/__tests__/Filters.test.ts @@ -1,8 +1,21 @@ import {describe, it, vi, expect, beforeEach} from "vitest" -import * as Filters from "../src/Filters" - -import type {TrustedEvent} from "../src/Events" import {GENERIC_REPOST, LONG_FORM, MUTES, REPOST} from "@welshman/util" +import { + addRepostFilters, + getFilterGenerality, + getFilterId, + getFilterResultCardinality, + getIdFilters, + getReplyFilters, + guessFilterDelta, + intersectFilters, + matchFilter, + matchFilters, + trimFilter, + unionFilters, + Filter, +} from "../src/Filters" +import type {TrustedEvent} from "../src/Events" describe("Filters", () => { beforeEach(() => { @@ -27,23 +40,23 @@ describe("Filters", () => { it("should match basic filter criteria", () => { const event = createEvent() const filter = {kinds: [1], authors: [pubkey]} - expect(Filters.matchFilter(filter, event)).toBe(true) + expect(matchFilter(filter, event)).toBe(true) }) it("should handle search terms", () => { const event = createEvent({content: "Hello Nostr World!"}) - expect(Filters.matchFilter({search: "nostr"}, event)).toBe(true) - expect(Filters.matchFilter({search: "bitcoin"}, event)).toBe(false) + expect(matchFilter({search: "nostr"}, event)).toBe(true) + expect(matchFilter({search: "bitcoin"}, event)).toBe(false) }) it("should handle multiple search terms", () => { const event = createEvent({content: "Hello Nostr World!"}) - expect(Filters.matchFilter({search: "hello world"}, event)).toBe(true) + expect(matchFilter({search: "hello world"}, event)).toBe(true) }) it("should handle case-insensitive search", () => { const event = createEvent({content: "Hello NOSTR World!"}) - expect(Filters.matchFilter({search: "nostr"}, event)).toBe(true) + expect(matchFilter({search: "nostr"}, event)).toBe(true) }) }) @@ -51,13 +64,13 @@ describe("Filters", () => { it("should match if any filter matches", () => { const event = createEvent() const filters = [{kinds: [2]}, {kinds: [1], authors: [pubkey]}] - expect(Filters.matchFilters(filters, event)).toBe(true) + expect(matchFilters(filters, event)).toBe(true) }) it("should not match if no filters match", () => { const event = createEvent() const filters = [{kinds: [2]}, {kinds: [3]}] - expect(Filters.matchFilters(filters, event)).toBe(false) + expect(matchFilters(filters, event)).toBe(false) }) }) @@ -65,13 +78,13 @@ describe("Filters", () => { it("should generate consistent IDs for equivalent filters", () => { const filter1 = {kinds: [1], authors: [pubkey]} const filter2 = {authors: [pubkey], kinds: [1]} - expect(Filters.getFilterId(filter1)).toBe(Filters.getFilterId(filter2)) + expect(getFilterId(filter1)).toBe(getFilterId(filter2)) }) it("should generate different IDs for different filters", () => { const filter1 = {kinds: [1], authors: [pubkey]} const filter2 = {kinds: [2], authors: [pubkey]} - expect(Filters.getFilterId(filter1)).not.toBe(Filters.getFilterId(filter2)) + expect(getFilterId(filter1)).not.toBe(getFilterId(filter2)) }) }) @@ -81,14 +94,14 @@ describe("Filters", () => { {kinds: [1], authors: [pubkey]}, {kinds: [1], authors: [pubkey + "1"]}, ] - const result = Filters.unionFilters(filters) + const result = unionFilters(filters) expect(result).toHaveLength(1) expect(result[0].authors).toHaveLength(2) }) it("should handle different filter groups", () => { - const filters = [{kinds: [1]}, {"#e": [id]}] - const result = Filters.unionFilters(filters) + const filters: Filter[] = [{kinds: [1]}, {"#e": [id]}] + const result = unionFilters(filters) expect(result).toHaveLength(2) }) @@ -97,7 +110,7 @@ describe("Filters", () => { {kinds: [1], limit: 10, since: 1000, until: 2000, search: "test"}, {kinds: [1], limit: 10, since: 1000, until: 2000, search: "test"}, ] - const result = Filters.unionFilters(filters) + const result = unionFilters(filters) expect(result).toHaveLength(1) expect(result[0]).toMatchObject({limit: 10, since: 1000, until: 2000, search: "test"}) }) @@ -106,7 +119,7 @@ describe("Filters", () => { describe("intersectFilters", () => { it("should combine filter groups", () => { const groups = [[{kinds: [1]}], [{authors: [pubkey]}]] - const result = Filters.intersectFilters(groups) + const result = intersectFilters(groups) expect(result).toHaveLength(1) expect(result[0]).toMatchObject({ kinds: [1], @@ -119,7 +132,7 @@ describe("Filters", () => { [{since: 1000, until: 2000, limit: 10}], [{since: 1500, until: 1800, limit: 20}], ] - const result = Filters.intersectFilters(groups) + const result = intersectFilters(groups) expect(result[0]).toMatchObject({ since: 1500, // Max of since until: 1800, // Min of until @@ -129,20 +142,20 @@ describe("Filters", () => { it("should combine search terms", () => { const groups = [[{search: "hello"}], [{search: "world"}]] - const result = Filters.intersectFilters(groups) + const result = intersectFilters(groups) expect(result[0].search).toBe("hello world") }) }) describe("getIdFilters", () => { it("should handle plain IDs", () => { - const result = Filters.getIdFilters([id]) + const result = getIdFilters([id]) expect(result[0].ids).toContain(id) }) it("should handle addresses", () => { const addr = `1:${pubkey}:test` - const result = Filters.getIdFilters([addr]) + const result = getIdFilters([addr]) expect(result[0]).toMatchObject({ kinds: [1], authors: [pubkey], @@ -152,7 +165,7 @@ describe("Filters", () => { it("should handle mixed IDs and addresses", () => { const addr = `1:${pubkey}:test` - const result = Filters.getIdFilters([id, addr]) + const result = getIdFilters([id, addr]) expect(result).toHaveLength(2) }) }) @@ -160,13 +173,13 @@ describe("Filters", () => { describe("getReplyFilters", () => { it("should create filters for regular events", () => { const event = createEvent() - const result = Filters.getReplyFilters([event]) + const result = getReplyFilters([event]) expect((result[0] as any)["#e"]).toContain(event.id) }) it("should handle replaceable events", () => { const event = createEvent({kind: MUTES}) - const result = Filters.getReplyFilters([event]) + const result = getReplyFilters([event]) expect((result[0] as any)["#a"]).toBeDefined() }) @@ -174,20 +187,20 @@ describe("Filters", () => { const event = createEvent({ wrap: createEvent(), }) - const result = Filters.getReplyFilters([event]) + const result = getReplyFilters([event]) expect((result[0] as any)["#e"]).toHaveLength(2) }) }) describe("addRepostFilters", () => { it("should add repost kinds for kind 1", () => { - const result = Filters.addRepostFilters([{kinds: [1]}]) + const result = addRepostFilters([{kinds: [1]}]) expect(result).toHaveLength(2) expect(result[1].kinds).toContain(REPOST) }) it("should handle other kinds", () => { - const result = Filters.addRepostFilters([{kinds: [LONG_FORM]}]) + const result = addRepostFilters([{kinds: [LONG_FORM]}]) expect(result[1].kinds).toContain(GENERIC_REPOST) expect(result[1].kinds).not.toContain(REPOST) expect(result[1]["#k"]).toContain(LONG_FORM.toString()) @@ -196,27 +209,27 @@ describe("Filters", () => { describe("filter utilities", () => { it("should calculate filter generality", () => { - expect(Filters.getFilterGenerality({ids: [id]})).toBe(0) - expect(Filters.getFilterGenerality({authors: [pubkey], "#p": [pubkey]})).toBe(0.2) - expect(Filters.getFilterGenerality({authors: [pubkey, pubkey, pubkey], kinds: [1]})).toBe( + expect(getFilterGenerality({ids: [id]})).toBe(0) + expect(getFilterGenerality({authors: [pubkey], "#p": [pubkey]})).toBe(0.2) + expect(getFilterGenerality({authors: [pubkey, pubkey, pubkey], kinds: [1]})).toBe( 0.01, ) - expect(Filters.getFilterGenerality({kinds: [1]})).toBe(1) + expect(getFilterGenerality({kinds: [1]})).toBe(1) }) it("should guess filter delta", () => { - const result = Filters.guessFilterDelta([{ids: [id]}]) + const result = guessFilterDelta([{ids: [id]}]) expect(result).toBeGreaterThan(0) }) it("should get filter result cardinality", () => { - expect(Filters.getFilterResultCardinality({ids: [id, id + "1"]})).toBe(2) - expect(Filters.getFilterResultCardinality({kinds: [1]})).toBeUndefined() + expect(getFilterResultCardinality({ids: [id, id + "1"]})).toBe(2) + expect(getFilterResultCardinality({kinds: [1]})).toBeUndefined() }) it("should trim large filters", () => { const largeFilter = {authors: Array(2000).fill(pubkey)} - const result = Filters.trimFilter(largeFilter) + const result = trimFilter(largeFilter) expect(result.authors?.length).toBe(1000) }) }) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6596c5d..ce639d1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -32,6 +32,15 @@ importers: prettier: specifier: ~3.5.3 version: 3.5.3 + typedoc: + specifier: ^0.28.2 + version: 0.28.2(typescript@5.8.2) + typedoc-plugin-markdown: + specifier: ^4.6.1 + version: 4.6.1(typedoc@0.28.2(typescript@5.8.2)) + typedoc-vitepress-theme: + specifier: ^1.1.2 + version: 1.1.2(typedoc-plugin-markdown@4.6.1(typedoc@0.28.2(typescript@5.8.2))) typescript: specifier: ~5.8.0 version: 5.8.2 @@ -43,7 +52,7 @@ importers: version: 1.6.3(@algolia/client-search@5.23.3)(@types/node@22.13.17)(fuse.js@7.1.0)(postcss@8.5.3)(search-insights@2.17.3)(typescript@5.8.2) vitest: specifier: ^3.1.1 - version: 3.1.1(@types/node@22.13.17)(happy-dom@17.4.4) + version: 3.1.1(@types/node@22.13.17)(happy-dom@17.4.4)(yaml@2.7.1) packages/app: dependencies: @@ -801,6 +810,9 @@ packages: resolution: {integrity: sha512-ZAoA40rNMPwSm+AeHpCq8STiNAwzWLJuP8Xv4CHIc9wv/PSuExjMrmjfYNj682vW0OOiZ1HKxzvjQr9XZIisQA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@gerrit0/mini-shiki@3.2.2': + resolution: {integrity: sha512-vaZNGhGLKMY14HbF53xxHNgFO9Wz+t5lTlGNpl2N9xFiKQ0I5oIe0vKjU9dh7Nb3Dw6lZ7wqUE0ri+zcdpnK+Q==} + '@humanfs/core@0.19.1': resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} engines: {node: '>=18.18.0'} @@ -1020,18 +1032,30 @@ packages: '@shikijs/engine-oniguruma@2.5.0': resolution: {integrity: sha512-pGd1wRATzbo/uatrCIILlAdFVKdxImWJGQ5rFiB5VZi2ve5xj3Ax9jny8QvkaV93btQEwR/rSz5ERFpC5mKNIw==} + '@shikijs/engine-oniguruma@3.2.1': + resolution: {integrity: sha512-wZZAkayEn6qu2+YjenEoFqj0OyQI64EWsNR6/71d1EkG4sxEOFooowKivsWPpaWNBu3sxAG+zPz5kzBL/SsreQ==} + '@shikijs/langs@2.5.0': resolution: {integrity: sha512-Qfrrt5OsNH5R+5tJ/3uYBBZv3SuGmnRPejV9IlIbFH3HTGLDlkqgHymAlzklVmKBjAaVmkPkyikAV/sQ1wSL+w==} + '@shikijs/langs@3.2.1': + resolution: {integrity: sha512-If0iDHYRSGbihiA8+7uRsgb1er1Yj11pwpX1c6HLYnizDsKAw5iaT3JXj5ZpaimXSWky/IhxTm7C6nkiYVym+A==} + '@shikijs/themes@2.5.0': resolution: {integrity: sha512-wGrk+R8tJnO0VMzmUExHR+QdSaPUl/NKs+a4cQQRWyoc3YFbUzuLEi/KWK1hj+8BfHRKm2jNhhJck1dfstJpiw==} + '@shikijs/themes@3.2.1': + resolution: {integrity: sha512-k5DKJUT8IldBvAm8WcrDT5+7GA7se6lLksR+2E3SvyqGTyFMzU2F9Gb7rmD+t+Pga1MKrYFxDIeyWjMZWM6uBQ==} + '@shikijs/transformers@2.5.0': resolution: {integrity: sha512-SI494W5X60CaUwgi8u4q4m4s3YAFSxln3tzNjOSYqq54wlVgz0/NbbXEb3mdLbqMBztcmS7bVTaEd2w0qMmfeg==} '@shikijs/types@2.5.0': resolution: {integrity: sha512-ygl5yhxki9ZLNuNpPitBWvcy9fsSKKaRuO4BAlMyagszQidxcpLAr0qiW/q43DtSIDxO6hEbtYLiFZNXO/hdGw==} + '@shikijs/types@3.2.1': + resolution: {integrity: sha512-/NTWAk4KE2M8uac0RhOsIhYQf4pdU0OywQuYDGIGAJ6Mjunxl2cGiuLkvu4HLCMn+OTTLRWkjZITp+aYJv60yA==} + '@shikijs/vscode-textmate@10.0.2': resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==} @@ -2065,6 +2089,9 @@ packages: resolution: {integrity: sha512-QIXZUBJUx+2zHUdQujWejBkcD9+cs94tLn0+YL8UrCh+D5sCXZ4c7LaEH48pNwRY3MLDgqUFyhlCyjJPf1WP0A==} engines: {node: 20 || >=22} + lunr@2.3.9: + resolution: {integrity: sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==} + magic-string@0.30.17: resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} @@ -2637,6 +2664,24 @@ packages: resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==} engines: {node: '>= 0.4'} + typedoc-plugin-markdown@4.6.1: + resolution: {integrity: sha512-SrJv9zkpCWdG1cvtWniFU6M7MkCZuheN2R3fuqDPF+O+PeZ8bzmfj1ju/BJwoPWIKyFJVPhK8Sg6tBrM1y+VoA==} + engines: {node: '>= 18'} + peerDependencies: + typedoc: 0.28.x + + typedoc-vitepress-theme@1.1.2: + resolution: {integrity: sha512-hQvCZRr5uKDqY1bRuY1+eNTNn6d4TE4OP5pnw65Y7WGgajkJW9X1/lVJK2UJpcwCmwkdjw1QIO49H9JQlxWhhw==} + peerDependencies: + typedoc-plugin-markdown: '>=4.4.0' + + typedoc@0.28.2: + resolution: {integrity: sha512-9Giuv+eppFKnJ0oi+vxqLM817b/IrIsEMYgy3jj6zdvppAfDqV3d6DXL2vXUg2TnlL62V48th25Zf/tcQKAJdg==} + engines: {node: '>= 18', pnpm: '>= 10'} + hasBin: true + peerDependencies: + typescript: 5.0.x || 5.1.x || 5.2.x || 5.3.x || 5.4.x || 5.5.x || 5.6.x || 5.7.x || 5.8.x + typescript-eslint@8.29.0: resolution: {integrity: sha512-ep9rVd9B4kQsZ7ZnWCVxUE/xDLUUUsRzE0poAeNu+4CkFErLfuvPt/qtm2EpnSyfvsR0S6QzDFSrPCFBwf64fg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -2868,6 +2913,11 @@ packages: utf-8-validate: optional: true + yaml@2.7.1: + resolution: {integrity: sha512-10ULxpnOCQXxJvBgxsn9ptjq6uviG/htZKk9veJGhlqn3w/DxQ631zFF+nlQXLwmImeS5amR2dl2U8sg6U9jsQ==} + engines: {node: '>= 14'} + hasBin: true + yocto-queue@0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} @@ -3222,6 +3272,14 @@ snapshots: '@eslint/core': 0.13.0 levn: 0.4.1 + '@gerrit0/mini-shiki@3.2.2': + dependencies: + '@shikijs/engine-oniguruma': 3.2.1 + '@shikijs/langs': 3.2.1 + '@shikijs/themes': 3.2.1 + '@shikijs/types': 3.2.1 + '@shikijs/vscode-textmate': 10.0.2 + '@humanfs/core@0.19.1': {} '@humanfs/node@0.16.6': @@ -3402,14 +3460,27 @@ snapshots: '@shikijs/types': 2.5.0 '@shikijs/vscode-textmate': 10.0.2 + '@shikijs/engine-oniguruma@3.2.1': + dependencies: + '@shikijs/types': 3.2.1 + '@shikijs/vscode-textmate': 10.0.2 + '@shikijs/langs@2.5.0': dependencies: '@shikijs/types': 2.5.0 + '@shikijs/langs@3.2.1': + dependencies: + '@shikijs/types': 3.2.1 + '@shikijs/themes@2.5.0': dependencies: '@shikijs/types': 2.5.0 + '@shikijs/themes@3.2.1': + dependencies: + '@shikijs/types': 3.2.1 + '@shikijs/transformers@2.5.0': dependencies: '@shikijs/core': 2.5.0 @@ -3420,6 +3491,11 @@ snapshots: '@shikijs/vscode-textmate': 10.0.2 '@types/hast': 3.0.4 + '@shikijs/types@3.2.1': + dependencies: + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.4 + '@shikijs/vscode-textmate@10.0.2': {} '@tiptap/core@2.11.7(@tiptap/pm@2.11.7)': @@ -3644,13 +3720,13 @@ snapshots: chai: 5.2.0 tinyrainbow: 2.0.0 - '@vitest/mocker@3.1.1(vite@6.2.5(@types/node@22.13.17))': + '@vitest/mocker@3.1.1(vite@6.2.5(@types/node@22.13.17)(yaml@2.7.1))': dependencies: '@vitest/spy': 3.1.1 estree-walker: 3.0.3 magic-string: 0.30.17 optionalDependencies: - vite: 6.2.5(@types/node@22.13.17) + vite: 6.2.5(@types/node@22.13.17)(yaml@2.7.1) '@vitest/pretty-format@3.1.1': dependencies: @@ -4688,6 +4764,8 @@ snapshots: lru-cache@11.1.0: {} + lunr@2.3.9: {} + magic-string@0.30.17: dependencies: '@jridgewell/sourcemap-codec': 1.5.0 @@ -5403,6 +5481,23 @@ snapshots: possible-typed-array-names: 1.1.0 reflect.getprototypeof: 1.0.10 + typedoc-plugin-markdown@4.6.1(typedoc@0.28.2(typescript@5.8.2)): + dependencies: + typedoc: 0.28.2(typescript@5.8.2) + + typedoc-vitepress-theme@1.1.2(typedoc-plugin-markdown@4.6.1(typedoc@0.28.2(typescript@5.8.2))): + dependencies: + typedoc-plugin-markdown: 4.6.1(typedoc@0.28.2(typescript@5.8.2)) + + typedoc@0.28.2(typescript@5.8.2): + dependencies: + '@gerrit0/mini-shiki': 3.2.2 + lunr: 2.3.9 + markdown-it: 14.1.0 + minimatch: 9.0.5 + typescript: 5.8.2 + yaml: 2.7.1 + typescript-eslint@8.29.0(eslint@9.23.0)(typescript@5.8.2): dependencies: '@typescript-eslint/eslint-plugin': 8.29.0(@typescript-eslint/parser@8.29.0(eslint@9.23.0)(typescript@5.8.2))(eslint@9.23.0)(typescript@5.8.2) @@ -5463,13 +5558,13 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.2 - vite-node@3.1.1(@types/node@22.13.17): + vite-node@3.1.1(@types/node@22.13.17)(yaml@2.7.1): dependencies: cac: 6.7.14 debug: 4.4.0 es-module-lexer: 1.6.0 pathe: 2.0.3 - vite: 6.2.5(@types/node@22.13.17) + vite: 6.2.5(@types/node@22.13.17)(yaml@2.7.1) transitivePeerDependencies: - '@types/node' - jiti @@ -5493,7 +5588,7 @@ snapshots: '@types/node': 22.13.17 fsevents: 2.3.3 - vite@6.2.5(@types/node@22.13.17): + vite@6.2.5(@types/node@22.13.17)(yaml@2.7.1): dependencies: esbuild: 0.25.2 postcss: 8.5.3 @@ -5501,6 +5596,7 @@ snapshots: optionalDependencies: '@types/node': 22.13.17 fsevents: 2.3.3 + yaml: 2.7.1 vitepress@1.6.3(@algolia/client-search@5.23.3)(@types/node@22.13.17)(fuse.js@7.1.0)(postcss@8.5.3)(search-insights@2.17.3)(typescript@5.8.2): dependencies: @@ -5551,10 +5647,10 @@ snapshots: - typescript - universal-cookie - vitest@3.1.1(@types/node@22.13.17)(happy-dom@17.4.4): + vitest@3.1.1(@types/node@22.13.17)(happy-dom@17.4.4)(yaml@2.7.1): dependencies: '@vitest/expect': 3.1.1 - '@vitest/mocker': 3.1.1(vite@6.2.5(@types/node@22.13.17)) + '@vitest/mocker': 3.1.1(vite@6.2.5(@types/node@22.13.17)(yaml@2.7.1)) '@vitest/pretty-format': 3.1.1 '@vitest/runner': 3.1.1 '@vitest/snapshot': 3.1.1 @@ -5570,8 +5666,8 @@ snapshots: tinyexec: 0.3.2 tinypool: 1.0.2 tinyrainbow: 2.0.0 - vite: 6.2.5(@types/node@22.13.17) - vite-node: 3.1.1(@types/node@22.13.17) + vite: 6.2.5(@types/node@22.13.17)(yaml@2.7.1) + vite-node: 3.1.1(@types/node@22.13.17)(yaml@2.7.1) why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 22.13.17 @@ -5672,6 +5768,8 @@ snapshots: ws@8.18.1: {} + yaml@2.7.1: {} + yocto-queue@0.1.0: {} zwitch@2.0.4: {} diff --git a/typedoc.json b/typedoc.json new file mode 100644 index 0000000..56ef1e1 --- /dev/null +++ b/typedoc.json @@ -0,0 +1,10 @@ +{ + "name": "Welshman Docs", + "out": "./docs/reference", + "docsRoot": "./docs", + "entryPoints": ["packages/*"], + "entryPointStrategy": "packages", + "exclude": ["**/normalize-url/*"], + "plugin": ["typedoc-plugin-markdown", "typedoc-vitepress-theme"], + "hideGenerator": true +}