Bring back typedoc

This commit is contained in:
Jon Staab
2025-04-08 15:08:00 -07:00
parent 02202d298e
commit 3301616e7d
19 changed files with 261 additions and 135 deletions
+4 -4
View File
@@ -1,9 +1,9 @@
node_modules node_modules
**/dist **/dist
**/cached docs/.vitepress/dist
**/cache docs/.vitepress/cache
**/temp docs/.vitepress/.temp
**/reference docs/reference
results results
.nyc_output .nyc_output
*.tsbuildinfo *.tsbuildinfo
+3
View File
@@ -23,6 +23,9 @@
"globals": "~16.0.0", "globals": "~16.0.0",
"happy-dom": "^17.4.4", "happy-dom": "^17.4.4",
"prettier": "~3.5.3", "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": "~5.8.0",
"typescript-eslint": "~8.29.0", "typescript-eslint": "~8.29.0",
"vitepress": "^1.6.3", "vitepress": "^1.6.3",
+1 -1
View File
@@ -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. Utilities for dealing with svelte stores when using welshman.
+1 -1
View File
@@ -179,7 +179,7 @@ describe("collection", () => {
getKey: item => item.id, getKey: item => item.id,
}) })
const derived = col.deriveItem(null) const derived = col.deriveItem(undefined)
expect(get(derived)).toBeUndefined() expect(get(derived)).toBeUndefined()
}) })
+2 -2
View File
@@ -1,5 +1,5 @@
import {now} from "@welshman/lib" 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 {NOTE, makeEvent} from "@welshman/util"
import {Nip01Signer} from "@welshman/signer" import {Nip01Signer} from "@welshman/signer"
import {LOCAL_RELAY_URL} from "@welshman/relay" import {LOCAL_RELAY_URL} from "@welshman/relay"
@@ -112,7 +112,7 @@ describe("thunk", () => {
const send = vi.fn() const send = vi.fn()
const track = vi.spyOn(tracker, 'track') const track = vi.spyOn(tracker, 'track')
const thunk = makeThunk(mockRequest) const thunk = makeThunk(mockRequest)
let status = {} let status: Record<string, any> = {}
// Subscribe to status updates // Subscribe to status updates
thunk.status.subscribe(_status => { thunk.status.subscribe(_status => {
+42 -38
View File
@@ -1,5 +1,5 @@
import {describe, it, expect} from "vitest" 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" import {npubEncode, noteEncode} from "nostr-tools/nip19"
describe("Content Parsing", () => { describe("Content Parsing", () => {
@@ -7,51 +7,55 @@ describe("Content Parsing", () => {
const nevent = noteEncode("ff".repeat(32)) const nevent = noteEncode("ff".repeat(32))
describe("Basic Parsing", () => { describe("Basic Parsing", () => {
it("should parse plain text", () => { it("should parse plain text", () => {
const result = Content.parse({content: "Hello world"}) const result = parse({content: "Hello world"})
expect(result).toEqual([ 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", () => { it("should parse newlines", () => {
const result = Content.parse({content: "Hello\nworld"}) const result = parse({content: "Hello\nworld"})
expect(result).toEqual([ expect(result).toEqual([
{type: Content.ParsedType.Text, value: "Hello", raw: "Hello"}, {type: ParsedType.Text, value: "Hello", raw: "Hello"},
{type: Content.ParsedType.Newline, value: "\n", raw: "\n"}, {type: ParsedType.Newline, value: "\n", raw: "\n"},
{type: Content.ParsedType.Text, value: "world", raw: "world"}, {type: ParsedType.Text, value: "world", raw: "world"},
]) ])
}) })
}) })
describe("Link Parsing", () => { describe("Link Parsing", () => {
it("should parse basic URLs", () => { it("should parse basic URLs", () => {
const result = Content.parse({content: "Check https://example.com"}) const result = parse({content: "Check https://example.com"})
expect(result[1]).toMatchObject({ const parsed = result[1] as ParsedLink
type: Content.ParsedType.Link,
expect(parsed).toMatchObject({
type: ParsedType.Link,
value: { value: {
url: expect.any(URL), 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", () => { it("should parse URLs without protocol", () => {
const result = Content.parse({content: "Visit example.com"}) const result = parse({content: "Visit example.com"})
expect(result[1]).toMatchObject({ const parsed = result[1] as ParsedLink
type: Content.ParsedType.Link,
expect(parsed).toMatchObject({
type: ParsedType.Link,
value: { value: {
url: expect.any(URL), 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", () => { 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({ expect(result[0]).toMatchObject({
type: Content.ParsedType.Link, type: ParsedType.Link,
value: { value: {
url: expect.any(URL), url: expect.any(URL),
meta: {}, meta: {},
@@ -62,46 +66,46 @@ describe("Content Parsing", () => {
describe("Nostr Entity Parsing", () => { describe("Nostr Entity Parsing", () => {
it("should parse nostr profiles", () => { it("should parse nostr profiles", () => {
const result = Content.parse({ const result = parse({
content: `nostr:${npub}`, content: `nostr:${npub}`,
}) })
expect(result[0]).toMatchObject({ expect(result[0]).toMatchObject({
type: Content.ParsedType.Profile, type: ParsedType.Profile,
}) })
}) })
it("should parse nostr events", () => { it("should parse nostr events", () => {
const result = Content.parse({ const result = parse({
content: `nostr:${nevent}`, content: `nostr:${nevent}`,
}) })
expect(result[0]).toMatchObject({ expect(result[0]).toMatchObject({
type: Content.ParsedType.Event, type: ParsedType.Event,
}) })
}) })
}) })
describe("Special Content Parsing", () => { describe("Special Content Parsing", () => {
it("should parse code blocks", () => { it("should parse code blocks", () => {
const result = Content.parse({content: "```const x = 1```"}) const result = parse({content: "```const x = 1```"})
expect(result[0]).toMatchObject({ expect(result[0]).toMatchObject({
type: Content.ParsedType.Code, type: ParsedType.Code,
value: "const x = 1", value: "const x = 1",
}) })
}) })
it("should parse inline code", () => { it("should parse inline code", () => {
const result = Content.parse({content: "Use `npm install`"}) const result = parse({content: "Use `npm install`"})
expect(result[1]).toMatchObject({ expect(result[1]).toMatchObject({
type: Content.ParsedType.Code, type: ParsedType.Code,
value: "npm install", value: "npm install",
}) })
}) })
it("should parse topics", () => { it("should parse topics", () => {
const result = Content.parse({content: "#nostr is cool"}) const result = parse({content: "#nostr is cool"})
expect(result[0]).toMatchObject({ expect(result[0]).toMatchObject({
type: Content.ParsedType.Topic, type: ParsedType.Topic,
value: "nostr", value: "nostr",
}) })
}) })
@@ -109,27 +113,27 @@ describe("Content Parsing", () => {
describe("Rendering", () => { describe("Rendering", () => {
it("should render as text", () => { it("should render as text", () => {
const parsed = Content.parse({content: "Hello https://example.com"}) const parsed = parse({content: "Hello https://example.com"})
const rendered = Content.renderAsText(parsed).toString() const rendered = renderAsText(parsed).toString()
expect(rendered).toContain("Hello") expect(rendered).toContain("Hello")
expect(rendered).toContain("https://example.com") expect(rendered).toContain("https://example.com")
}) })
it("should render as HTML", () => { it("should render as HTML", () => {
const parsed = Content.parse({content: "Hello https://example.com"}) const parsed = parse({content: "Hello https://example.com"})
const rendered = Content.renderAsHtml(parsed).toString() const rendered = renderAsHtml(parsed).toString()
expect(rendered).toContain('<a href="https://example.com/"') expect(rendered).toContain('<a href="https://example.com/"')
}) })
}) })
describe("Link Grid", () => { describe("Link Grid", () => {
it("should reduce consecutive image links into a grid", () => { 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", 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({ expect(reduced[0]).toMatchObject({
type: Content.ParsedType.LinkGrid, type: ParsedType.LinkGrid,
value: { value: {
links: expect.any(Array), links: expect.any(Array),
}, },
@@ -139,12 +143,12 @@ describe("Content Parsing", () => {
describe("Legacy Mention Parsing", () => { describe("Legacy Mention Parsing", () => {
it("should parse legacy mentions", () => { it("should parse legacy mentions", () => {
const result = Content.parse({ const result = parse({
content: "#[0]", content: "#[0]",
tags: [["p", "1234567890"]], tags: [["p", "1234567890"]],
}) })
expect(result[0]).toMatchObject({ expect(result[0]).toMatchObject({
type: Content.ParsedType.Profile, type: ParsedType.Profile,
}) })
}) })
}) })
+2 -4
View File
@@ -36,7 +36,6 @@ describe("Content Truncation", () => {
value: { value: {
url: new URL("https://example.com/image.jpg"), url: new URL("https://example.com/image.jpg"),
meta: {}, meta: {},
isMedia: true,
}, },
raw: "https://example.com/image.jpg", raw: "https://example.com/image.jpg",
}, },
@@ -83,7 +82,6 @@ describe("Content Truncation", () => {
value: { value: {
url: new URL("https://example.com/image.jpg"), url: new URL("https://example.com/image.jpg"),
meta: {}, meta: {},
isMedia: true,
}, },
raw: "https://example.com/image.jpg", raw: "https://example.com/image.jpg",
}, },
@@ -141,8 +139,8 @@ describe("Content Truncation", () => {
type: ParsedType.LinkGrid, type: ParsedType.LinkGrid,
value: { value: {
links: [ links: [
{url: new URL("https://example.com/1.jpg"), meta: {}, isMedia: true}, {url: new URL("https://example.com/1.jpg"), meta: {}},
{url: new URL("https://example.com/2.jpg"), meta: {}, isMedia: true}, {url: new URL("https://example.com/2.jpg"), meta: {}},
], ],
}, },
raw: "", raw: "",
+5 -5
View File
@@ -1,6 +1,6 @@
import {defaultTagFeedMappings} from "@welshman/feeds" import {defaultTagFeedMappings} from "@welshman/feeds"
import {now} from "@welshman/lib" 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 {beforeEach, describe, expect, it, vi} from "vitest"
import {FeedCompiler} from "../src/compiler" import {FeedCompiler} from "../src/compiler"
import {Feed, FeedType, Scope} from "../src/core" import {Feed, FeedType, Scope} from "../src/core"
@@ -269,7 +269,7 @@ describe("FeedCompiler", () => {
sig: "sig1", sig: "sig1",
} }
mockOptions.requestDVM.mockImplementation(async ({onEvent}) => { mockOptions.requestDVM.mockImplementation(async ({onEvent}: any) => {
await onEvent(mockEvent) await onEvent(mockEvent)
}) })
@@ -307,7 +307,7 @@ describe("FeedCompiler", () => {
sig: "sig1", sig: "sig1",
} }
mockOptions.request.mockImplementation(({onEvent}) => { mockOptions.request.mockImplementation(({onEvent}: any) => {
onEvent(mockEvent) onEvent(mockEvent)
}) })
@@ -345,7 +345,7 @@ describe("FeedCompiler", () => {
sig: "sig1", sig: "sig1",
} }
mockOptions.request.mockImplementation(({onEvent}) => { mockOptions.request.mockImplementation(({onEvent}: any) => {
onEvent(labelEvent) onEvent(labelEvent)
}) })
@@ -381,7 +381,7 @@ describe("FeedCompiler", () => {
sig: "sig1", sig: "sig1",
} }
mockOptions.requestDVM.mockImplementation(async ({onEvent}) => { mockOptions.requestDVM.mockImplementation(async ({onEvent}: any) => {
await onEvent(mockEvent) await onEvent(mockEvent)
}) })
+1 -1
View File
@@ -1,5 +1,5 @@
import {Tracker} from "../src/Tracker"
import {vi, describe, it, expect, beforeEach} from "vitest" import {vi, describe, it, expect, beforeEach} from "vitest"
import {Tracker} from "../src/tracker"
describe("Tracker", () => { describe("Tracker", () => {
let tracker: Tracker let tracker: Tracker
+10 -8
View File
@@ -1,20 +1,20 @@
import EventEmitter from "events" import EventEmitter from "events"
import { describe, expect, it, vi, beforeEach, afterEach } from "vitest" import { describe, expect, it, vi, beforeEach, afterEach } from "vitest"
import { isRelayUrl } from "@welshman/util" 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 { AdapterEvent, SocketAdapter, LocalAdapter, getAdapter } from "../src/adapter"
import { ClientMessage, RelayMessage } from "../src/message" import { ClientMessage, RelayMessage } from "../src/message"
import { Socket, SocketEvent } from "../src/socket" import { Socket, SocketEvent } from "../src/socket"
import { Pool } from "../src/pool" import { Pool } from "../src/pool"
vi.mock('isomorphic-ws', () => { vi.mock('isomorphic-ws', () => {
const WebSocket = vi.fn(function () { const WebSocket = vi.fn(function (this: any) {
setTimeout(() => this.onopen()) setTimeout(() => this.onopen())
}) })
WebSocket.prototype.send = vi.fn() WebSocket.prototype.send = vi.fn()
WebSocket.prototype.close = vi.fn(function () { WebSocket.prototype.close = vi.fn(function (this: any) {
this.onclose() this.onclose()
}) })
@@ -120,12 +120,14 @@ describe("LocalAdapter", () => {
describe("getAdapter", () => { describe("getAdapter", () => {
let pool: Pool let pool: Pool
let relay: LocalRelay let repository: Repository
beforeEach(() => { beforeEach(() => {
pool = new Pool() pool = new Pool({
relay = new LocalRelay() makeSocket: () => new Socket("wss://test.relay"),
pool.get = vi.fn().mockReturnValue(new Socket("wss://test.relay")) })
repository = new Repository()
}) })
afterEach(() => { afterEach(() => {
@@ -134,7 +136,7 @@ describe("getAdapter", () => {
it("should return LocalAdapter for local relay URL", () => { it("should return LocalAdapter for local relay URL", () => {
const url = LOCAL_RELAY_URL const url = LOCAL_RELAY_URL
const adapter = getAdapter(url, { relay }) const adapter = getAdapter(url, { repository })
expect(adapter).toBeInstanceOf(LocalAdapter) expect(adapter).toBeInstanceOf(LocalAdapter)
}) })
+3 -3
View File
@@ -1,6 +1,6 @@
import { describe, expect, it, vi, beforeEach, afterEach } from "vitest" import { describe, expect, it, vi, beforeEach, afterEach } from "vitest"
import { Socket, SocketStatus, SocketEvent } from "../src/socket" 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 { Nip01Signer } from "@welshman/signer"
import { AuthState, AuthStatus, AuthStateEvent, makeAuthEvent } from "../src/auth" import { AuthState, AuthStatus, AuthStateEvent, makeAuthEvent } from "../src/auth"
import EventEmitter from "events" import EventEmitter from "events"
@@ -146,7 +146,7 @@ describe('auth', () => {
socket.auth.challenge = "challenge123" socket.auth.challenge = "challenge123"
socket.auth.status = AuthStatus.Requested socket.auth.status = AuthStatus.Requested
const sign = async e => { const sign = async (e: StampedEvent) => {
event = await Nip01Signer.ephemeral().sign(e) event = await Nip01Signer.ephemeral().sign(e)
return event return event
@@ -154,7 +154,7 @@ describe('auth', () => {
await socket.auth.authenticate(sign) await socket.auth.authenticate(sign)
expect(socket.auth.request).toStrictEqual(event.id) expect(socket.auth.request).toStrictEqual(event!.id)
expect(sendSpy).toHaveBeenCalledWith(["AUTH", event]) expect(sendSpy).toHaveBeenCalledWith(["AUTH", event])
}) })
}) })
+4 -4
View File
@@ -4,13 +4,13 @@ import { Pool, makeSocket } from "../src/pool"
import { normalizeRelayUrl } from "@welshman/util" import { normalizeRelayUrl } from "@welshman/util"
vi.mock('isomorphic-ws', () => { vi.mock('isomorphic-ws', () => {
const WebSocket = vi.fn(function () { const WebSocket = vi.fn(function (this: any) {
setTimeout(() => this.onopen()) setTimeout(() => this.onopen())
}) })
WebSocket.prototype.send = vi.fn() WebSocket.prototype.send = vi.fn()
WebSocket.prototype.close = vi.fn(function () { WebSocket.prototype.close = vi.fn(function (this: any) {
this.onclose() this.onclose()
}) })
@@ -97,7 +97,7 @@ describe("Pool", () => {
it("should remove and cleanup existing socket", () => { it("should remove and cleanup existing socket", () => {
const mockSocket = { url: "wss://test.relay", cleanup: vi.fn() } 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) pool.remove(mockSocket.url)
expect(mockSocket.cleanup).toHaveBeenCalled() expect(mockSocket.cleanup).toHaveBeenCalled()
@@ -116,7 +116,7 @@ describe("Pool", () => {
const mockSockets = urls.map(url => ({ url, cleanup: vi.fn() })) const mockSockets = urls.map(url => ({ url, cleanup: vi.fn() }))
for (const mockSocket of mockSockets) { for (const mockSocket of mockSockets) {
pool._data.set(mockSocket.url, mockSocket) pool._data.set(mockSocket.url, mockSocket as unknown as Socket)
} }
pool.clear() pool.clear()
+4 -4
View File
@@ -103,14 +103,14 @@ describe("SinglePublish", () => {
pub.on(PublishEvent.Complete, completeSpy) pub.on(PublishEvent.Complete, completeSpy)
pub.on(PublishEvent.Timeout, timeoutSpy) pub.on(PublishEvent.Timeout, timeoutSpy)
await vi.runAllTimers(200) await vi.runAllTimers()
expect(sendSpy).toHaveBeenCalledWith([ClientMessageType.Event, event]) expect(sendSpy).toHaveBeenCalledWith([ClientMessageType.Event, event])
await vi.runAllTimers() await vi.runAllTimers()
expect(successSpy).not.toHaveBeenCalled() expect(successSpy).not.toHaveBeenCalled()
expect(failureSpy).not.toHaveBeenCalled(event.id, "hi") expect(failureSpy).not.toHaveBeenCalled()
expect(completeSpy).toHaveBeenCalled() expect(completeSpy).toHaveBeenCalled()
expect(timeoutSpy).toHaveBeenCalled() expect(timeoutSpy).toHaveBeenCalled()
}) })
@@ -137,7 +137,7 @@ describe("SinglePublish", () => {
pub.on(PublishEvent.Complete, completeSpy) pub.on(PublishEvent.Complete, completeSpy)
pub.on(PublishEvent.Timeout, abortSpy) pub.on(PublishEvent.Timeout, abortSpy)
await vi.runAllTimers(200) await vi.runAllTimers()
expect(sendSpy).toHaveBeenCalledWith([ClientMessageType.Event, event]) expect(sendSpy).toHaveBeenCalledWith([ClientMessageType.Event, event])
@@ -146,7 +146,7 @@ describe("SinglePublish", () => {
await vi.runAllTimers() await vi.runAllTimers()
expect(successSpy).not.toHaveBeenCalled() expect(successSpy).not.toHaveBeenCalled()
expect(failureSpy).not.toHaveBeenCalled(event.id, "hi") expect(failureSpy).not.toHaveBeenCalled()
expect(completeSpy).toHaveBeenCalled() expect(completeSpy).toHaveBeenCalled()
expect(abortSpy).toHaveBeenCalled() expect(abortSpy).toHaveBeenCalled()
}) })
+10 -13
View File
@@ -5,13 +5,13 @@ import { Socket, SocketStatus, SocketEvent } from "../src/socket"
import { ClientMessage, RelayMessage } from "../src/message" import { ClientMessage, RelayMessage } from "../src/message"
vi.mock('isomorphic-ws', () => { vi.mock('isomorphic-ws', () => {
const WebSocket = vi.fn(function () { const WebSocket = vi.fn(function (this: any) {
setTimeout(() => this.onopen()) setTimeout(() => this.onopen())
}) })
WebSocket.prototype.send = vi.fn() WebSocket.prototype.send = vi.fn()
WebSocket.prototype.close = vi.fn(function () { WebSocket.prototype.close = vi.fn(function (this: any) {
this.onclose() this.onclose()
}) })
@@ -77,11 +77,9 @@ describe("Socket", () => {
socket.open() socket.open()
const ws = socket._ws
socket.close() socket.close()
expect(ws.close).toHaveBeenCalled() expect(socket._ws!.close).toHaveBeenCalled()
expect(statusSpy).toHaveBeenCalledWith(SocketStatus.Closed, "wss://test.relay") expect(statusSpy).toHaveBeenCalledWith(SocketStatus.Closed, "wss://test.relay")
}) })
}) })
@@ -102,14 +100,14 @@ describe("Socket", () => {
socket.on(SocketEvent.Send, sendSpy) socket.on(SocketEvent.Send, sendSpy)
socket.open() socket.open()
socket._ws.onopen() socket._ws?.onopen?.(undefined as unknown as any)
const message: ClientMessage = ["EVENT", { id: "123", kind: 1 }] const message: ClientMessage = ["EVENT", { id: "123", kind: 1 }]
socket.send(message) socket.send(message)
await vi.runAllTimers() 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") expect(sendSpy).toHaveBeenCalledWith(message, "wss://test.relay")
}) })
}) })
@@ -121,9 +119,8 @@ describe("Socket", () => {
socket.open() socket.open()
const message: RelayMessage = ["EVENT", "123", { id: "123", kind: 1 }] 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() await vi.runAllTimers()
expect(receiveSpy).toHaveBeenCalledWith(message, "wss://test.relay") expect(receiveSpy).toHaveBeenCalledWith(message, "wss://test.relay")
@@ -134,7 +131,7 @@ describe("Socket", () => {
socket.on(SocketEvent.Error, errorSpy) socket.on(SocketEvent.Error, errorSpy)
socket.open() 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") expect(errorSpy).toHaveBeenCalledWith("Invalid message received", "wss://test.relay")
}) })
@@ -144,7 +141,7 @@ describe("Socket", () => {
socket.on(SocketEvent.Error, errorSpy) socket.on(SocketEvent.Error, errorSpy)
socket.open() 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") expect(errorSpy).toHaveBeenCalledWith("Invalid message received", "wss://test.relay")
}) })
@@ -158,7 +155,7 @@ describe("Socket", () => {
socket.cleanup() socket.cleanup()
expect(ws.close).toHaveBeenCalled() expect(ws!.close).toHaveBeenCalled()
expect(socket.listenerCount(SocketEvent.Send)).toBe(0) expect(socket.listenerCount(SocketEvent.Send)).toBe(0)
}) })
}) })
@@ -169,7 +166,7 @@ describe("Socket", () => {
socket.on(SocketEvent.Status, statusSpy) socket.on(SocketEvent.Status, statusSpy)
socket.open() socket.open()
socket._ws.onerror() socket._ws?.onerror?.(undefined as unknown as any)
expect(statusSpy).toHaveBeenCalledWith(SocketStatus.Error, "wss://test.relay") expect(statusSpy).toHaveBeenCalledWith(SocketStatus.Error, "wss://test.relay")
}) })
+1 -1
View File
@@ -1,7 +1,7 @@
import {describe, it, vi, expect, beforeEach} from "vitest" import {describe, it, vi, expect, beforeEach} from "vitest"
import {now} from "@welshman/lib" import {now} from "@welshman/lib"
import {getAddress, TrustedEvent, DELETE, MUTES} from "@welshman/util" import {getAddress, TrustedEvent, DELETE, MUTES} from "@welshman/util"
import {Repository} from "../src/Repository" import {Repository} from "../src/repository"
describe("Repository", () => { describe("Repository", () => {
beforeEach(() => { beforeEach(() => {
+2 -1
View File
@@ -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 {get} from "svelte/store"
import {afterEach, beforeEach, describe, expect, it, vi} from "vitest" import {afterEach, beforeEach, describe, expect, it, vi} from "vitest"
import { import {
+48 -35
View File
@@ -1,8 +1,21 @@
import {describe, it, vi, expect, beforeEach} from "vitest" 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 {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", () => { describe("Filters", () => {
beforeEach(() => { beforeEach(() => {
@@ -27,23 +40,23 @@ describe("Filters", () => {
it("should match basic filter criteria", () => { it("should match basic filter criteria", () => {
const event = createEvent() const event = createEvent()
const filter = {kinds: [1], authors: [pubkey]} const filter = {kinds: [1], authors: [pubkey]}
expect(Filters.matchFilter(filter, event)).toBe(true) expect(matchFilter(filter, event)).toBe(true)
}) })
it("should handle search terms", () => { it("should handle search terms", () => {
const event = createEvent({content: "Hello Nostr World!"}) const event = createEvent({content: "Hello Nostr World!"})
expect(Filters.matchFilter({search: "nostr"}, event)).toBe(true) expect(matchFilter({search: "nostr"}, event)).toBe(true)
expect(Filters.matchFilter({search: "bitcoin"}, event)).toBe(false) expect(matchFilter({search: "bitcoin"}, event)).toBe(false)
}) })
it("should handle multiple search terms", () => { it("should handle multiple search terms", () => {
const event = createEvent({content: "Hello Nostr World!"}) 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", () => { it("should handle case-insensitive search", () => {
const event = createEvent({content: "Hello NOSTR World!"}) 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", () => { it("should match if any filter matches", () => {
const event = createEvent() const event = createEvent()
const filters = [{kinds: [2]}, {kinds: [1], authors: [pubkey]}] 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", () => { it("should not match if no filters match", () => {
const event = createEvent() const event = createEvent()
const filters = [{kinds: [2]}, {kinds: [3]}] 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", () => { it("should generate consistent IDs for equivalent filters", () => {
const filter1 = {kinds: [1], authors: [pubkey]} const filter1 = {kinds: [1], authors: [pubkey]}
const filter2 = {authors: [pubkey], kinds: [1]} 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", () => { it("should generate different IDs for different filters", () => {
const filter1 = {kinds: [1], authors: [pubkey]} const filter1 = {kinds: [1], authors: [pubkey]}
const filter2 = {kinds: [2], 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]},
{kinds: [1], authors: [pubkey + "1"]}, {kinds: [1], authors: [pubkey + "1"]},
] ]
const result = Filters.unionFilters(filters) const result = unionFilters(filters)
expect(result).toHaveLength(1) expect(result).toHaveLength(1)
expect(result[0].authors).toHaveLength(2) expect(result[0].authors).toHaveLength(2)
}) })
it("should handle different filter groups", () => { it("should handle different filter groups", () => {
const filters = [{kinds: [1]}, {"#e": [id]}] const filters: Filter[] = [{kinds: [1]}, {"#e": [id]}]
const result = Filters.unionFilters(filters) const result = unionFilters(filters)
expect(result).toHaveLength(2) 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"},
{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).toHaveLength(1)
expect(result[0]).toMatchObject({limit: 10, since: 1000, until: 2000, search: "test"}) expect(result[0]).toMatchObject({limit: 10, since: 1000, until: 2000, search: "test"})
}) })
@@ -106,7 +119,7 @@ describe("Filters", () => {
describe("intersectFilters", () => { describe("intersectFilters", () => {
it("should combine filter groups", () => { it("should combine filter groups", () => {
const groups = [[{kinds: [1]}], [{authors: [pubkey]}]] const groups = [[{kinds: [1]}], [{authors: [pubkey]}]]
const result = Filters.intersectFilters(groups) const result = intersectFilters(groups)
expect(result).toHaveLength(1) expect(result).toHaveLength(1)
expect(result[0]).toMatchObject({ expect(result[0]).toMatchObject({
kinds: [1], kinds: [1],
@@ -119,7 +132,7 @@ describe("Filters", () => {
[{since: 1000, until: 2000, limit: 10}], [{since: 1000, until: 2000, limit: 10}],
[{since: 1500, until: 1800, limit: 20}], [{since: 1500, until: 1800, limit: 20}],
] ]
const result = Filters.intersectFilters(groups) const result = intersectFilters(groups)
expect(result[0]).toMatchObject({ expect(result[0]).toMatchObject({
since: 1500, // Max of since since: 1500, // Max of since
until: 1800, // Min of until until: 1800, // Min of until
@@ -129,20 +142,20 @@ describe("Filters", () => {
it("should combine search terms", () => { it("should combine search terms", () => {
const groups = [[{search: "hello"}], [{search: "world"}]] const groups = [[{search: "hello"}], [{search: "world"}]]
const result = Filters.intersectFilters(groups) const result = intersectFilters(groups)
expect(result[0].search).toBe("hello world") expect(result[0].search).toBe("hello world")
}) })
}) })
describe("getIdFilters", () => { describe("getIdFilters", () => {
it("should handle plain IDs", () => { it("should handle plain IDs", () => {
const result = Filters.getIdFilters([id]) const result = getIdFilters([id])
expect(result[0].ids).toContain(id) expect(result[0].ids).toContain(id)
}) })
it("should handle addresses", () => { it("should handle addresses", () => {
const addr = `1:${pubkey}:test` const addr = `1:${pubkey}:test`
const result = Filters.getIdFilters([addr]) const result = getIdFilters([addr])
expect(result[0]).toMatchObject({ expect(result[0]).toMatchObject({
kinds: [1], kinds: [1],
authors: [pubkey], authors: [pubkey],
@@ -152,7 +165,7 @@ describe("Filters", () => {
it("should handle mixed IDs and addresses", () => { it("should handle mixed IDs and addresses", () => {
const addr = `1:${pubkey}:test` const addr = `1:${pubkey}:test`
const result = Filters.getIdFilters([id, addr]) const result = getIdFilters([id, addr])
expect(result).toHaveLength(2) expect(result).toHaveLength(2)
}) })
}) })
@@ -160,13 +173,13 @@ describe("Filters", () => {
describe("getReplyFilters", () => { describe("getReplyFilters", () => {
it("should create filters for regular events", () => { it("should create filters for regular events", () => {
const event = createEvent() const event = createEvent()
const result = Filters.getReplyFilters([event]) const result = getReplyFilters([event])
expect((result[0] as any)["#e"]).toContain(event.id) expect((result[0] as any)["#e"]).toContain(event.id)
}) })
it("should handle replaceable events", () => { it("should handle replaceable events", () => {
const event = createEvent({kind: MUTES}) const event = createEvent({kind: MUTES})
const result = Filters.getReplyFilters([event]) const result = getReplyFilters([event])
expect((result[0] as any)["#a"]).toBeDefined() expect((result[0] as any)["#a"]).toBeDefined()
}) })
@@ -174,20 +187,20 @@ describe("Filters", () => {
const event = createEvent({ const event = createEvent({
wrap: createEvent(), wrap: createEvent(),
}) })
const result = Filters.getReplyFilters([event]) const result = getReplyFilters([event])
expect((result[0] as any)["#e"]).toHaveLength(2) expect((result[0] as any)["#e"]).toHaveLength(2)
}) })
}) })
describe("addRepostFilters", () => { describe("addRepostFilters", () => {
it("should add repost kinds for kind 1", () => { 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).toHaveLength(2)
expect(result[1].kinds).toContain(REPOST) expect(result[1].kinds).toContain(REPOST)
}) })
it("should handle other kinds", () => { 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).toContain(GENERIC_REPOST)
expect(result[1].kinds).not.toContain(REPOST) expect(result[1].kinds).not.toContain(REPOST)
expect(result[1]["#k"]).toContain(LONG_FORM.toString()) expect(result[1]["#k"]).toContain(LONG_FORM.toString())
@@ -196,27 +209,27 @@ describe("Filters", () => {
describe("filter utilities", () => { describe("filter utilities", () => {
it("should calculate filter generality", () => { it("should calculate filter generality", () => {
expect(Filters.getFilterGenerality({ids: [id]})).toBe(0) expect(getFilterGenerality({ids: [id]})).toBe(0)
expect(Filters.getFilterGenerality({authors: [pubkey], "#p": [pubkey]})).toBe(0.2) expect(getFilterGenerality({authors: [pubkey], "#p": [pubkey]})).toBe(0.2)
expect(Filters.getFilterGenerality({authors: [pubkey, pubkey, pubkey], kinds: [1]})).toBe( expect(getFilterGenerality({authors: [pubkey, pubkey, pubkey], kinds: [1]})).toBe(
0.01, 0.01,
) )
expect(Filters.getFilterGenerality({kinds: [1]})).toBe(1) expect(getFilterGenerality({kinds: [1]})).toBe(1)
}) })
it("should guess filter delta", () => { it("should guess filter delta", () => {
const result = Filters.guessFilterDelta([{ids: [id]}]) const result = guessFilterDelta([{ids: [id]}])
expect(result).toBeGreaterThan(0) expect(result).toBeGreaterThan(0)
}) })
it("should get filter result cardinality", () => { it("should get filter result cardinality", () => {
expect(Filters.getFilterResultCardinality({ids: [id, id + "1"]})).toBe(2) expect(getFilterResultCardinality({ids: [id, id + "1"]})).toBe(2)
expect(Filters.getFilterResultCardinality({kinds: [1]})).toBeUndefined() expect(getFilterResultCardinality({kinds: [1]})).toBeUndefined()
}) })
it("should trim large filters", () => { it("should trim large filters", () => {
const largeFilter = {authors: Array(2000).fill(pubkey)} const largeFilter = {authors: Array(2000).fill(pubkey)}
const result = Filters.trimFilter(largeFilter) const result = trimFilter(largeFilter)
expect(result.authors?.length).toBe(1000) expect(result.authors?.length).toBe(1000)
}) })
}) })
+108 -10
View File
@@ -32,6 +32,15 @@ importers:
prettier: prettier:
specifier: ~3.5.3 specifier: ~3.5.3
version: 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: typescript:
specifier: ~5.8.0 specifier: ~5.8.0
version: 5.8.2 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) 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: vitest:
specifier: ^3.1.1 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: packages/app:
dependencies: dependencies:
@@ -801,6 +810,9 @@ packages:
resolution: {integrity: sha512-ZAoA40rNMPwSm+AeHpCq8STiNAwzWLJuP8Xv4CHIc9wv/PSuExjMrmjfYNj682vW0OOiZ1HKxzvjQr9XZIisQA==} resolution: {integrity: sha512-ZAoA40rNMPwSm+AeHpCq8STiNAwzWLJuP8Xv4CHIc9wv/PSuExjMrmjfYNj682vW0OOiZ1HKxzvjQr9XZIisQA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 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': '@humanfs/core@0.19.1':
resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==}
engines: {node: '>=18.18.0'} engines: {node: '>=18.18.0'}
@@ -1020,18 +1032,30 @@ packages:
'@shikijs/engine-oniguruma@2.5.0': '@shikijs/engine-oniguruma@2.5.0':
resolution: {integrity: sha512-pGd1wRATzbo/uatrCIILlAdFVKdxImWJGQ5rFiB5VZi2ve5xj3Ax9jny8QvkaV93btQEwR/rSz5ERFpC5mKNIw==} 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': '@shikijs/langs@2.5.0':
resolution: {integrity: sha512-Qfrrt5OsNH5R+5tJ/3uYBBZv3SuGmnRPejV9IlIbFH3HTGLDlkqgHymAlzklVmKBjAaVmkPkyikAV/sQ1wSL+w==} 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': '@shikijs/themes@2.5.0':
resolution: {integrity: sha512-wGrk+R8tJnO0VMzmUExHR+QdSaPUl/NKs+a4cQQRWyoc3YFbUzuLEi/KWK1hj+8BfHRKm2jNhhJck1dfstJpiw==} 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': '@shikijs/transformers@2.5.0':
resolution: {integrity: sha512-SI494W5X60CaUwgi8u4q4m4s3YAFSxln3tzNjOSYqq54wlVgz0/NbbXEb3mdLbqMBztcmS7bVTaEd2w0qMmfeg==} resolution: {integrity: sha512-SI494W5X60CaUwgi8u4q4m4s3YAFSxln3tzNjOSYqq54wlVgz0/NbbXEb3mdLbqMBztcmS7bVTaEd2w0qMmfeg==}
'@shikijs/types@2.5.0': '@shikijs/types@2.5.0':
resolution: {integrity: sha512-ygl5yhxki9ZLNuNpPitBWvcy9fsSKKaRuO4BAlMyagszQidxcpLAr0qiW/q43DtSIDxO6hEbtYLiFZNXO/hdGw==} resolution: {integrity: sha512-ygl5yhxki9ZLNuNpPitBWvcy9fsSKKaRuO4BAlMyagszQidxcpLAr0qiW/q43DtSIDxO6hEbtYLiFZNXO/hdGw==}
'@shikijs/types@3.2.1':
resolution: {integrity: sha512-/NTWAk4KE2M8uac0RhOsIhYQf4pdU0OywQuYDGIGAJ6Mjunxl2cGiuLkvu4HLCMn+OTTLRWkjZITp+aYJv60yA==}
'@shikijs/vscode-textmate@10.0.2': '@shikijs/vscode-textmate@10.0.2':
resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==} resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==}
@@ -2065,6 +2089,9 @@ packages:
resolution: {integrity: sha512-QIXZUBJUx+2zHUdQujWejBkcD9+cs94tLn0+YL8UrCh+D5sCXZ4c7LaEH48pNwRY3MLDgqUFyhlCyjJPf1WP0A==} resolution: {integrity: sha512-QIXZUBJUx+2zHUdQujWejBkcD9+cs94tLn0+YL8UrCh+D5sCXZ4c7LaEH48pNwRY3MLDgqUFyhlCyjJPf1WP0A==}
engines: {node: 20 || >=22} engines: {node: 20 || >=22}
lunr@2.3.9:
resolution: {integrity: sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==}
magic-string@0.30.17: magic-string@0.30.17:
resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==}
@@ -2637,6 +2664,24 @@ packages:
resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==} resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==}
engines: {node: '>= 0.4'} 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: typescript-eslint@8.29.0:
resolution: {integrity: sha512-ep9rVd9B4kQsZ7ZnWCVxUE/xDLUUUsRzE0poAeNu+4CkFErLfuvPt/qtm2EpnSyfvsR0S6QzDFSrPCFBwf64fg==} resolution: {integrity: sha512-ep9rVd9B4kQsZ7ZnWCVxUE/xDLUUUsRzE0poAeNu+4CkFErLfuvPt/qtm2EpnSyfvsR0S6QzDFSrPCFBwf64fg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
@@ -2868,6 +2913,11 @@ packages:
utf-8-validate: utf-8-validate:
optional: true optional: true
yaml@2.7.1:
resolution: {integrity: sha512-10ULxpnOCQXxJvBgxsn9ptjq6uviG/htZKk9veJGhlqn3w/DxQ631zFF+nlQXLwmImeS5amR2dl2U8sg6U9jsQ==}
engines: {node: '>= 14'}
hasBin: true
yocto-queue@0.1.0: yocto-queue@0.1.0:
resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
engines: {node: '>=10'} engines: {node: '>=10'}
@@ -3222,6 +3272,14 @@ snapshots:
'@eslint/core': 0.13.0 '@eslint/core': 0.13.0
levn: 0.4.1 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/core@0.19.1': {}
'@humanfs/node@0.16.6': '@humanfs/node@0.16.6':
@@ -3402,14 +3460,27 @@ snapshots:
'@shikijs/types': 2.5.0 '@shikijs/types': 2.5.0
'@shikijs/vscode-textmate': 10.0.2 '@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': '@shikijs/langs@2.5.0':
dependencies: dependencies:
'@shikijs/types': 2.5.0 '@shikijs/types': 2.5.0
'@shikijs/langs@3.2.1':
dependencies:
'@shikijs/types': 3.2.1
'@shikijs/themes@2.5.0': '@shikijs/themes@2.5.0':
dependencies: dependencies:
'@shikijs/types': 2.5.0 '@shikijs/types': 2.5.0
'@shikijs/themes@3.2.1':
dependencies:
'@shikijs/types': 3.2.1
'@shikijs/transformers@2.5.0': '@shikijs/transformers@2.5.0':
dependencies: dependencies:
'@shikijs/core': 2.5.0 '@shikijs/core': 2.5.0
@@ -3420,6 +3491,11 @@ snapshots:
'@shikijs/vscode-textmate': 10.0.2 '@shikijs/vscode-textmate': 10.0.2
'@types/hast': 3.0.4 '@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': {} '@shikijs/vscode-textmate@10.0.2': {}
'@tiptap/core@2.11.7(@tiptap/pm@2.11.7)': '@tiptap/core@2.11.7(@tiptap/pm@2.11.7)':
@@ -3644,13 +3720,13 @@ snapshots:
chai: 5.2.0 chai: 5.2.0
tinyrainbow: 2.0.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: dependencies:
'@vitest/spy': 3.1.1 '@vitest/spy': 3.1.1
estree-walker: 3.0.3 estree-walker: 3.0.3
magic-string: 0.30.17 magic-string: 0.30.17
optionalDependencies: 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': '@vitest/pretty-format@3.1.1':
dependencies: dependencies:
@@ -4688,6 +4764,8 @@ snapshots:
lru-cache@11.1.0: {} lru-cache@11.1.0: {}
lunr@2.3.9: {}
magic-string@0.30.17: magic-string@0.30.17:
dependencies: dependencies:
'@jridgewell/sourcemap-codec': 1.5.0 '@jridgewell/sourcemap-codec': 1.5.0
@@ -5403,6 +5481,23 @@ snapshots:
possible-typed-array-names: 1.1.0 possible-typed-array-names: 1.1.0
reflect.getprototypeof: 1.0.10 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): typescript-eslint@8.29.0(eslint@9.23.0)(typescript@5.8.2):
dependencies: 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) '@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 '@types/unist': 3.0.3
vfile-message: 4.0.2 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: dependencies:
cac: 6.7.14 cac: 6.7.14
debug: 4.4.0 debug: 4.4.0
es-module-lexer: 1.6.0 es-module-lexer: 1.6.0
pathe: 2.0.3 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: transitivePeerDependencies:
- '@types/node' - '@types/node'
- jiti - jiti
@@ -5493,7 +5588,7 @@ snapshots:
'@types/node': 22.13.17 '@types/node': 22.13.17
fsevents: 2.3.3 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: dependencies:
esbuild: 0.25.2 esbuild: 0.25.2
postcss: 8.5.3 postcss: 8.5.3
@@ -5501,6 +5596,7 @@ snapshots:
optionalDependencies: optionalDependencies:
'@types/node': 22.13.17 '@types/node': 22.13.17
fsevents: 2.3.3 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): 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: dependencies:
@@ -5551,10 +5647,10 @@ snapshots:
- typescript - typescript
- universal-cookie - 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: dependencies:
'@vitest/expect': 3.1.1 '@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/pretty-format': 3.1.1
'@vitest/runner': 3.1.1 '@vitest/runner': 3.1.1
'@vitest/snapshot': 3.1.1 '@vitest/snapshot': 3.1.1
@@ -5570,8 +5666,8 @@ snapshots:
tinyexec: 0.3.2 tinyexec: 0.3.2
tinypool: 1.0.2 tinypool: 1.0.2
tinyrainbow: 2.0.0 tinyrainbow: 2.0.0
vite: 6.2.5(@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) vite-node: 3.1.1(@types/node@22.13.17)(yaml@2.7.1)
why-is-node-running: 2.3.0 why-is-node-running: 2.3.0
optionalDependencies: optionalDependencies:
'@types/node': 22.13.17 '@types/node': 22.13.17
@@ -5672,6 +5768,8 @@ snapshots:
ws@8.18.1: {} ws@8.18.1: {}
yaml@2.7.1: {}
yocto-queue@0.1.0: {} yocto-queue@0.1.0: {}
zwitch@2.0.4: {} zwitch@2.0.4: {}
+10
View File
@@ -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
}