From 728ad1fba0df4d03a2428773cbaad0d1f817a9bd Mon Sep 17 00:00:00 2001 From: Jon Staab Date: Wed, 9 Apr 2025 11:35:09 -0700 Subject: [PATCH] Lint --- .prettierrc | 3 +- eslint.config.mjs | 40 +- package.json | 2 +- packages/app/__tests__/thunk.test.ts | 16 +- packages/app/src/adapters.ts | 38 +- packages/app/src/feeds.ts | 4 +- packages/app/src/follows.ts | 2 - packages/app/src/handles.ts | 1 - packages/app/src/mutes.ts | 2 - packages/app/src/pins.ts | 2 - packages/app/src/profiles.ts | 4 +- packages/app/src/relaySelections.ts | 18 +- packages/app/src/router.ts | 8 +- packages/app/src/storage.ts | 9 +- packages/app/src/thunk.ts | 20 +- packages/app/src/zappers.ts | 1 - packages/content/__tests__/content.test.ts | 4 +- packages/editor/src/index.ts | 4 +- packages/lib/src/Tools.ts | 5 +- packages/lib/src/normalize-url/index.ts | 514 ++++----- packages/net/__tests__/adapter.test.ts | 42 +- packages/net/__tests__/auth.test.ts | 29 +- packages/net/__tests__/policy.test.ts | 107 +- packages/net/__tests__/pool.test.ts | 15 +- packages/net/__tests__/publish.test.ts | 54 +- packages/net/__tests__/request.test.ts | 33 +- packages/net/__tests__/socket.test.ts | 23 +- packages/net/src/negentropy.ts | 1007 ++++++++--------- packages/net/src/policy.ts | 7 +- packages/net/src/request.ts | 2 +- packages/relay/__tests__/relay.test.ts | 1 - packages/store/__tests__/index.test.ts | 23 - packages/store/src/index.ts | 16 - packages/util/__tests__/Events.test.ts | 2 +- packages/util/__tests__/Filters.test.ts | 4 +- packages/util/src/Events.ts | 3 +- pnpm-lock.yaml | 1157 -------------------- 37 files changed, 1039 insertions(+), 2183 deletions(-) diff --git a/.prettierrc b/.prettierrc index f281d3e..cf8124b 100644 --- a/.prettierrc +++ b/.prettierrc @@ -3,6 +3,5 @@ "printWidth": 100, "bracketSameLine": true, "arrowParens": "avoid", - "bracketSpacing": false, - "pluginSearchDirs": false + "bracketSpacing": false } diff --git a/eslint.config.mjs b/eslint.config.mjs index 351f332..8164bec 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -1,32 +1,32 @@ -import globals from "globals"; -import js from "@eslint/js"; -import tsEslint from "typescript-eslint"; -import pluginReact from "eslint-plugin-react"; -import eslintPluginPrettierRecommended from "eslint-plugin-prettier/recommended"; +import globals from "globals" +import js from "@eslint/js" +import tsEslint from "typescript-eslint" +import eslintPluginPrettierRecommended from "eslint-plugin-prettier/recommended" export default tsEslint.config( { - ignores: [ - "node_modules", - "!.*", - "**/dist", - "**/build", - ], + ignores: ["node_modules", "!.*", "**/dist", "**/build", "docs"], }, { extends: [js.configs.recommended, ...tsEslint.configs.recommended], files: ["**/*.{js,mjs,cjs,ts,jsx,tsx}"], - languageOptions: { globals: globals.node }, + languageOptions: {globals: globals.node}, rules: { + "no-useless-escape": "off", "@typescript-eslint/no-explicit-any": "off", "@typescript-eslint/ban-ts-comment": "off", - "@typescript-eslint/no-unused-vars": ["error", { - "vars": "all", - "args": "after-used", - "caughtErrors": "none", - "argsIgnorePattern": "^_", - }] - } + "@typescript-eslint/no-unused-vars": [ + "error", + { + vars: "all", + args: "none", + caughtErrors: "none", + argsIgnorePattern: "^_", + varsIgnorePattern: "^_", + ignoreRestSiblings: true, + }, + ], + }, }, eslintPluginPrettierRecommended, -); +) diff --git a/package.json b/package.json index 68b522e..d397b88 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ "clean": "pnpm run -r clean", "build": "pnpm run -r build", "link": "for p in $(ls packages); do cd packages/$p; pnpm link --global; cd ../..; done", + "format": "eslint --fix .", "lint": "eslint .", "test": "vitest", "docs:dev": "vitepress dev docs", @@ -19,7 +20,6 @@ "eslint": "~9.23.0", "eslint-config-prettier": "^10.1.1", "eslint-plugin-prettier": "~5.2.5", - "eslint-plugin-react": "~7.37.4", "fake-indexeddb": "^6.0.0", "globals": "~16.0.0", "happy-dom": "^17.4.4", diff --git a/packages/app/__tests__/thunk.test.ts b/packages/app/__tests__/thunk.test.ts index 9f1264a..41b818a 100644 --- a/packages/app/__tests__/thunk.test.ts +++ b/packages/app/__tests__/thunk.test.ts @@ -1,10 +1,7 @@ -import {now} from "@welshman/lib" -import {PublishStatus, MockAdapter} from "@welshman/net" -import {NOTE, makeEvent} from "@welshman/util" -import {Nip01Signer} from "@welshman/signer" +import {PublishStatus} from "@welshman/net" +import {NOTE, makeEvent} from "@welshman/util" import {LOCAL_RELAY_URL} from "@welshman/relay" import {getPubkey, makeSecret} from "@welshman/signer" -import {EventEmitter} from "events" import {afterEach, beforeEach, describe, expect, it, vi} from "vitest" import {repository, tracker} from "../src/core" import {addSession, dropSession} from "../src/session" @@ -31,7 +28,7 @@ const mockRequest = { describe("thunk", () => { beforeEach(() => { vi.useFakeTimers() - addSession({method: 'nip01', secret, pubkey}) + addSession({method: "nip01", secret, pubkey}) }) afterEach(async () => { @@ -70,7 +67,7 @@ describe("thunk", () => { describe("publishThunk", () => { it("should create and publish a thunk", async () => { - const publishSpy = vi.spyOn(repository, 'publish') + const publishSpy = vi.spyOn(repository, "publish") const result = publishThunk(mockRequest) expect(publishSpy).toHaveBeenCalled() @@ -79,7 +76,7 @@ describe("thunk", () => { }) it("should handle abort", () => { - const removeEventSpy = vi.spyOn(repository, 'removeEvent') + const removeEventSpy = vi.spyOn(repository, "removeEvent") const thunk = publishThunk(mockRequest) thunk.controller.abort() @@ -109,8 +106,7 @@ describe("thunk", () => { }) it("should update status during publishing", async () => { - const send = vi.fn() - const track = vi.spyOn(tracker, 'track') + const track = vi.spyOn(tracker, "track") const thunk = makeThunk(mockRequest) let status: Record = {} diff --git a/packages/app/src/adapters.ts b/packages/app/src/adapters.ts index 2a90355..a01ffb8 100644 --- a/packages/app/src/adapters.ts +++ b/packages/app/src/adapters.ts @@ -1,17 +1,25 @@ -import {get, derived} from 'svelte/store' -import {batch, fromPairs} from '@welshman/lib' -import {PROFILE, FOLLOWS, MUTES, RELAYS, INBOX_RELAYS, getPubkeyTagValues, getListTags} from '@welshman/util' -import {throttled, withGetter} from '@welshman/store' -import {RepositoryUpdate} from '@welshman/relay' -import {getAll, bulkPut, bulkDelete} from './storage.js' -import {relays} from './relays.js' -import {handles, onHandle} from './handles.js' -import {zappers, onZapper} from './zappers.js' -import {plaintext} from './plaintext.js' -import {freshness} from './freshness.js' -import {repository} from './core.js' -import {sessions} from './session.js' -import {userFollows} from './user.js' +import {derived} from "svelte/store" +import {batch, fromPairs} from "@welshman/lib" +import { + PROFILE, + FOLLOWS, + MUTES, + RELAYS, + INBOX_RELAYS, + getPubkeyTagValues, + getListTags, +} from "@welshman/util" +import {throttled, withGetter} from "@welshman/store" +import {RepositoryUpdate} from "@welshman/relay" +import {getAll, bulkPut, bulkDelete} from "./storage.js" +import {relays} from "./relays.js" +import {handles, onHandle} from "./handles.js" +import {zappers, onZapper} from "./zappers.js" +import {plaintext} from "./plaintext.js" +import {freshness} from "./freshness.js" +import {repository} from "./core.js" +import {sessions} from "./session.js" +import {userFollows} from "./user.js" export const defaultStorageAdapters = { relays: { @@ -70,7 +78,7 @@ export const defaultStorageAdapters = { init: async () => repository.load(await getAll("events")), sync: () => { const userFollowPubkeys = withGetter( - derived(userFollows, l => new Set(getPubkeyTagValues(getListTags(l)))) + derived(userFollows, l => new Set(getPubkeyTagValues(getListTags(l)))), ) const onUpdate = async ({added, removed}: RepositoryUpdate) => { diff --git a/packages/app/src/feeds.ts b/packages/app/src/feeds.ts index a0433ba..7992b17 100644 --- a/packages/app/src/feeds.ts +++ b/packages/app/src/feeds.ts @@ -33,7 +33,9 @@ export const requestDVM = async ({kind, onEvent, ...request}: DVMOpts) => { const tags = request.tags || [] const $signer = signer.get() || new Nip01Signer(makeSecret()) const pubkey = await $signer.getPubkey() - const relays = request.relays || Router.get().FromPubkeys(getPubkeyTagValues(tags)).policy(addMinimalFallbacks).getUrls() + const relays = + request.relays || + Router.get().FromPubkeys(getPubkeyTagValues(tags)).policy(addMinimalFallbacks).getUrls() if (!tags.some(nthEq(0, "expiration"))) { tags.push(["expiration", String(now() + 60)]) diff --git a/packages/app/src/follows.ts b/packages/app/src/follows.ts index ac81081..edcd844 100644 --- a/packages/app/src/follows.ts +++ b/packages/app/src/follows.ts @@ -1,9 +1,7 @@ import {FOLLOWS, asDecryptedEvent, readList} from "@welshman/util" import {TrustedEvent, PublishedList} from "@welshman/util" -import {MultiRequestOptions, load} from "@welshman/net" import {deriveEventsMapped} from "@welshman/store" import {repository} from "./core.js" -import {Router} from "./router.js" import {collection} from "./collection.js" import {loadWithAsapMetaRelayUrls} from "./relaySelections.js" diff --git a/packages/app/src/handles.ts b/packages/app/src/handles.ts index 2e4ed86..4171bb5 100644 --- a/packages/app/src/handles.ts +++ b/packages/app/src/handles.ts @@ -1,5 +1,4 @@ import {writable, derived} from "svelte/store" -import {MultiRequestOptions} from "@welshman/net" import {tryCatch, fetchJson, uniq, batcher, postJson, last} from "@welshman/lib" import {collection} from "./collection.js" import {deriveProfile} from "./profiles.js" diff --git a/packages/app/src/mutes.ts b/packages/app/src/mutes.ts index 5a2dd4e..f676b83 100644 --- a/packages/app/src/mutes.ts +++ b/packages/app/src/mutes.ts @@ -1,9 +1,7 @@ import {MUTES, asDecryptedEvent, readList} from "@welshman/util" import {TrustedEvent, PublishedList} from "@welshman/util" -import {load, MultiRequestOptions} from "@welshman/net" import {deriveEventsMapped} from "@welshman/store" import {repository} from "./core.js" -import {Router} from "./router.js" import {collection} from "./collection.js" import {ensurePlaintext} from "./plaintext.js" import {loadWithAsapMetaRelayUrls} from "./relaySelections.js" diff --git a/packages/app/src/pins.ts b/packages/app/src/pins.ts index be3a3ca..11e802a 100644 --- a/packages/app/src/pins.ts +++ b/packages/app/src/pins.ts @@ -1,9 +1,7 @@ import {PINS, asDecryptedEvent, readList} from "@welshman/util" import {TrustedEvent, PublishedList} from "@welshman/util" -import {load, MultiRequestOptions} from "@welshman/net" import {deriveEventsMapped} from "@welshman/store" import {repository} from "./core.js" -import {Router} from "./router.js" import {collection} from "./collection.js" import {loadWithAsapMetaRelayUrls} from "./relaySelections.js" diff --git a/packages/app/src/profiles.ts b/packages/app/src/profiles.ts index cc5d98e..e8037cf 100644 --- a/packages/app/src/profiles.ts +++ b/packages/app/src/profiles.ts @@ -1,10 +1,8 @@ import {derived, readable} from "svelte/store" import {readProfile, displayProfile, displayPubkey, PROFILE} from "@welshman/util" -import {load, MultiRequestOptions} from "@welshman/net" import {PublishedProfile} from "@welshman/util" import {deriveEventsMapped, withGetter} from "@welshman/store" import {repository} from "./core.js" -import {Router} from "./router.js" import {collection} from "./collection.js" import {loadWithAsapMetaRelayUrls} from "./relaySelections.js" @@ -25,7 +23,7 @@ export const { store: profiles, getKey: profile => profile.event.pubkey, load: (pubkey: string, relays: string[]) => - loadWithAsapMetaRelayUrls(pubkey, relays, [{kinds: [PROFILE], authors: [pubkey]}]) + loadWithAsapMetaRelayUrls(pubkey, relays, [{kinds: [PROFILE], authors: [pubkey]}]), }) export const displayProfileByPubkey = (pubkey: string | undefined) => diff --git a/packages/app/src/relaySelections.ts b/packages/app/src/relaySelections.ts index e72c135..f8cbb14 100644 --- a/packages/app/src/relaySelections.ts +++ b/packages/app/src/relaySelections.ts @@ -10,10 +10,10 @@ import { getRelayTagValues, } from "@welshman/util" import {TrustedEvent, Filter, PublishedList, List} from "@welshman/util" -import {load, MultiRequestOptions} from "@welshman/net" +import {load} from "@welshman/net" import {deriveEventsMapped} from "@welshman/store" import {repository} from "./core.js" -import {Router, addNoFallbacks} from "./router.js" +import {Router} from "./router.js" import {collection} from "./collection.js" export const getRelayUrls = (list?: List): string[] => @@ -51,13 +51,15 @@ export const { const router = Router.get() await load({ - relays: router.merge([router.Index(), router.FromRelays(relays), router.FromPubkey(pubkey)]).getUrls(), + relays: router + .merge([router.Index(), router.FromRelays(relays), router.FromPubkey(pubkey)]) + .getUrls(), filters: [{kinds: [RELAYS], authors: [pubkey]}], }) }, }) -export const loadWithAsapMetaRelayUrls = (pubkey: string, relays: string[], filters: Filter[]) => { +export const loadWithAsapMetaRelayUrls = (pubkey: string, relays: string[], filters: Filter[]) => { const router = Router.get() return new Promise(resolve => { @@ -69,8 +71,10 @@ export const loadWithAsapMetaRelayUrls = (pubkey: string, relays: string[], f } } - load({filters, relays: router.merge([router.Index(), router.FromRelays(relays)]).getUrls()}) - .then(onLoad) + load({ + filters, + relays: router.merge([router.Index(), router.FromRelays(relays)]).getUrls(), + }).then(onLoad) loadRelaySelections(pubkey, relays) .then(() => load({filters, relays: router.FromPubkey(pubkey).getUrls()})) @@ -93,5 +97,5 @@ export const { store: inboxRelaySelections, getKey: inboxRelaySelections => inboxRelaySelections.event.pubkey, load: (pubkey: string, relays: string[]) => - loadWithAsapMetaRelayUrls(pubkey, relays, [{kinds: [INBOX_RELAYS], authors: [pubkey]}]) + loadWithAsapMetaRelayUrls(pubkey, relays, [{kinds: [INBOX_RELAYS], authors: [pubkey]}]), }) diff --git a/packages/app/src/router.ts b/packages/app/src/router.ts index 6005887..2e5c276 100644 --- a/packages/app/src/router.ts +++ b/packages/app/src/router.ts @@ -14,7 +14,6 @@ import { MINUTE, HOUR, DAY, - WEEK, } from "@welshman/lib" import { getFilterId, @@ -242,7 +241,8 @@ export class Router { FromPubkey = (pubkey: string) => this.FromRelays(this.getRelaysForPubkey(pubkey, RelayMode.Write)) - PubkeyInbox = (pubkey: string) => this.FromRelays(this.getRelaysForPubkey(pubkey, RelayMode.Inbox)) + PubkeyInbox = (pubkey: string) => + this.FromRelays(this.getRelaysForPubkey(pubkey, RelayMode.Inbox)) ForPubkeys = (pubkeys: string[]) => this.merge(pubkeys.map(pubkey => this.ForPubkey(pubkey))) @@ -493,7 +493,9 @@ export const getFilterSelections = ( const result = [] for (const [id, filter] of filtersById.entries()) { - const scenario = Router.get().merge(scenariosById.get(id) || []).policy(addMinimalFallbacks) + const scenario = Router.get() + .merge(scenariosById.get(id) || []) + .policy(addMinimalFallbacks) result.push({filters: [filter], relays: scenario.getUrls()}) } diff --git a/packages/app/src/storage.ts b/packages/app/src/storage.ts index 82737cc..4c06444 100644 --- a/packages/app/src/storage.ts +++ b/packages/app/src/storage.ts @@ -1,12 +1,9 @@ import {openDB, deleteDB} from "idb" import {IDBPDatabase} from "idb" import {writable} from "svelte/store" -import {Unsubscriber, Writable} from "svelte/store" -import {indexBy, call, equals, throttle, fromPairs} from "@welshman/lib" -import {TrustedEvent} from "@welshman/util" -import {Repository} from "@welshman/relay" -import {Tracker} from "@welshman/net" -import {withGetter, adapter, throttled, custom} from "@welshman/store" +import {Unsubscriber} from "svelte/store" +import {call} from "@welshman/lib" +import {withGetter} from "@welshman/store" export type StorageAdapterOptions = { throttle?: number diff --git a/packages/app/src/thunk.ts b/packages/app/src/thunk.ts index bfe7ab5..f623164 100644 --- a/packages/app/src/thunk.ts +++ b/packages/app/src/thunk.ts @@ -1,5 +1,15 @@ import {Writable, Readable, writable, derived, get} from "svelte/store" -import {Deferred, fromPairs, TaskQueue, dissoc, identity, uniq, defer, sleep, assoc} from "@welshman/lib" +import { + Deferred, + fromPairs, + TaskQueue, + dissoc, + identity, + uniq, + defer, + sleep, + assoc, +} from "@welshman/lib" import {stamp, own, hash} from "@welshman/signer" import { TrustedEvent, @@ -225,9 +235,11 @@ export const thunkQueue = new TaskQueue({ // Update status to pending thunk.status.set( fromPairs( - thunk.request.relays - .map(url => [url, {status: PublishStatus.Pending, message: "Sending your message..."}]) - ) + thunk.request.relays.map(url => [ + url, + {status: PublishStatus.Pending, message: "Sending your message..."}, + ]), + ), ) // Send it off diff --git a/packages/app/src/zappers.ts b/packages/app/src/zappers.ts index 687637a..f374c72 100644 --- a/packages/app/src/zappers.ts +++ b/packages/app/src/zappers.ts @@ -1,6 +1,5 @@ import {writable, derived} from "svelte/store" import {Zapper} from "@welshman/util" -import {MultiRequestOptions} from "@welshman/net" import { identity, fetchJson, diff --git a/packages/content/__tests__/content.test.ts b/packages/content/__tests__/content.test.ts index a457067..36d9b92 100644 --- a/packages/content/__tests__/content.test.ts +++ b/packages/content/__tests__/content.test.ts @@ -8,9 +8,7 @@ describe("Content Parsing", () => { describe("Basic Parsing", () => { it("should parse plain text", () => { const result = parse({content: "Hello world"}) - expect(result).toEqual([ - {type: ParsedType.Text, value: "Hello world", raw: "Hello world"}, - ]) + expect(result).toEqual([{type: ParsedType.Text, value: "Hello world", raw: "Hello world"}]) }) it("should parse newlines", () => { diff --git a/packages/editor/src/index.ts b/packages/editor/src/index.ts index 4e9f783..f305d63 100644 --- a/packages/editor/src/index.ts +++ b/packages/editor/src/index.ts @@ -1,5 +1,5 @@ export * from "./nodeviews/index.js" export * from "./extensions/index.js" export * from "./plugins/index.js" -export {Editor, NodeViewProps} from '@tiptap/core' -export {UploadTask} from 'nostr-editor' +export {Editor, NodeViewProps} from "@tiptap/core" +export {UploadTask} from "nostr-editor" diff --git a/packages/lib/src/Tools.ts b/packages/lib/src/Tools.ts index e4dca91..955931a 100644 --- a/packages/lib/src/Tools.ts +++ b/packages/lib/src/Tools.ts @@ -1,6 +1,6 @@ import {bech32, utf8} from "@scure/base" -type Obj = Record; +type Obj = Record // ---------------------------------------------------------------------------- // Basic functional programming utilities @@ -134,7 +134,8 @@ export const gt = (x: number | undefined, y: number | undefined) => num(x) > num export const gte = (x: number | undefined, y: number | undefined) => num(x) >= num(y) /** Returns maximum value in array, handling undefined values */ -export const max = (xs: (number | undefined)[]) => xs.reduce((a: number, b) => Math.max(num(a), num(b)), 0) +export const max = (xs: (number | undefined)[]) => + xs.reduce((a: number, b) => Math.max(num(a), num(b)), 0) /** Returns minimum value in array, handling undefined values */ export const min = (xs: (number | undefined)[]) => { diff --git a/packages/lib/src/normalize-url/index.ts b/packages/lib/src/normalize-url/index.ts index f6b6bdb..dbc0855 100644 --- a/packages/lib/src/normalize-url/index.ts +++ b/packages/lib/src/normalize-url/index.ts @@ -1,12 +1,13 @@ // Copied from https://github.com/sindresorhus/normalize-url +/* eslint-disable */ export type Options = { - /** + /** @default 'http' */ - readonly defaultProtocol?: 'https' | 'http'; + readonly defaultProtocol?: "https" | "http" - /** + /** Prepends `defaultProtocol` to the URL if it's protocol-relative. @default true @@ -20,9 +21,9 @@ export type Options = { //=> '//sindresorhus.com' ``` */ - readonly normalizeProtocol?: boolean; + readonly normalizeProtocol?: boolean - /** + /** Normalizes HTTPS URLs to HTTP. @default false @@ -36,9 +37,9 @@ export type Options = { //=> 'http://sindresorhus.com' ``` */ - readonly forceHttp?: boolean; + readonly forceHttp?: boolean - /** + /** Normalizes HTTP URLs to HTTPS. This option cannot be used with the `forceHttp` option at the same time. @@ -54,9 +55,9 @@ export type Options = { //=> 'https://sindresorhus.com' ``` */ - readonly forceHttps?: boolean; + readonly forceHttps?: boolean - /** + /** Strip the [authentication](https://en.wikipedia.org/wiki/Basic_access_authentication) part of a URL. @default true @@ -70,9 +71,9 @@ export type Options = { //=> 'https://user:password@sindresorhus.com' ``` */ - readonly stripAuthentication?: boolean; + readonly stripAuthentication?: boolean - /** + /** Removes hash from the URL. @default false @@ -86,9 +87,9 @@ export type Options = { //=> 'http://sindresorhus.com/about.html' ``` */ - readonly stripHash?: boolean; + readonly stripHash?: boolean - /** + /** Remove the protocol from the URL: `http://sindresorhus.com` → `sindresorhus.com`. It will only remove `https://` and `http://` protocols. @@ -104,9 +105,9 @@ export type Options = { //=> 'sindresorhus.com' ``` */ - readonly stripProtocol?: boolean; + readonly stripProtocol?: boolean - /** + /** Strip the [text fragment](https://web.dev/text-fragments/) part of the URL __Note:__ The text fragment will always be removed if the `stripHash` option is set to `true`, as the hash contains the text fragment. @@ -128,9 +129,9 @@ export type Options = { //=> 'http://sindresorhus.com/about.html#section:~:text=hello' ``` */ - readonly stripTextFragment?: boolean; + readonly stripTextFragment?: boolean - /** + /** Removes `www.` from the URL. @default true @@ -144,9 +145,9 @@ export type Options = { //=> 'http://www.sindresorhus.com' ``` */ - readonly stripWWW?: boolean; + readonly stripWWW?: boolean - /** + /** Removes query parameters that matches any of the provided strings or regexes. @default [/^utm_\w+/i] @@ -177,9 +178,9 @@ export type Options = { //=> 'http://www.sindresorhus.com/?foo=bar&ref=test_ref&utm_medium=test' ``` */ - readonly removeQueryParameters?: ReadonlyArray | boolean; + readonly removeQueryParameters?: ReadonlyArray | boolean - /** + /** Keeps only query parameters that matches any of the provided strings or regexes. __Note__: It overrides the `removeQueryParameters` option. @@ -194,9 +195,9 @@ export type Options = { //=> 'https://sindresorhus.com/?ref=unicorn' ``` */ - readonly keepQueryParameters?: ReadonlyArray; + readonly keepQueryParameters?: ReadonlyArray - /** + /** Removes trailing slash. __Note__: Trailing slash is always removed if the URL doesn't have a pathname unless the `removeSingleSlash` option is set to `false`. @@ -215,9 +216,9 @@ export type Options = { //=> 'http://sindresorhus.com' ``` */ - readonly removeTrailingSlash?: boolean; + readonly removeTrailingSlash?: boolean - /** + /** Remove a sole `/` pathname in the output. This option is independent of `removeTrailingSlash`. @default true @@ -231,9 +232,9 @@ export type Options = { //=> 'https://sindresorhus.com/' ``` */ - readonly removeSingleSlash?: boolean; + readonly removeSingleSlash?: boolean - /** + /** Removes the default directory index file from path that matches any of the provided strings or regexes. When `true`, the regex `/^index\.[a-z]+$/` is used. @@ -247,9 +248,9 @@ export type Options = { //=> 'http://sindresorhus.com/foo' ``` */ - readonly removeDirectoryIndex?: boolean | ReadonlyArray; + readonly removeDirectoryIndex?: boolean | ReadonlyArray - /** + /** Removes an explicit port number from the URL. Port 443 is always removed from HTTPS URLs and 80 is always removed from HTTP URLs regardless of this option. @@ -264,9 +265,9 @@ export type Options = { //=> 'http://sindresorhus.com' ``` */ - readonly removeExplicitPort?: boolean; + readonly removeExplicitPort?: boolean - /** + /** Sorts the query parameters alphabetically by key. @default true @@ -279,79 +280,74 @@ export type Options = { //=> 'http://sindresorhus.com/?b=two&a=one&c=three' ``` */ - readonly sortQueryParameters?: boolean; -}; + readonly sortQueryParameters?: boolean +} // https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs -const DATA_URL_DEFAULT_MIME_TYPE = 'text/plain' -const DATA_URL_DEFAULT_CHARSET = 'us-ascii' +const DATA_URL_DEFAULT_MIME_TYPE = "text/plain" +const DATA_URL_DEFAULT_CHARSET = "us-ascii" -const testParameter = (name: string, filters: any[]) => filters.some(filter => filter instanceof RegExp ? filter.test(name) : filter === name) +const testParameter = (name: string, filters: any[]) => + filters.some(filter => (filter instanceof RegExp ? filter.test(name) : filter === name)) -const supportedProtocols = new Set([ - 'https:', - 'http:', - 'file:', -]) +const supportedProtocols = new Set(["https:", "http:", "file:"]) const hasCustomProtocol = (urlString: string) => { - try { - const {protocol} = new URL(urlString) - return protocol.endsWith(':') && !supportedProtocols.has(protocol) - } catch { - return false - } + try { + const {protocol} = new URL(urlString) + return protocol.endsWith(":") && !supportedProtocols.has(protocol) + } catch { + return false + } } const normalizeDataURL = (urlString: string, {stripHash}: {stripHash: boolean}) => { - const match = /^data:(?[^,]*?),(?[^#]*?)(?:#(?.*))?$/.exec(urlString) + const match = /^data:(?[^,]*?),(?[^#]*?)(?:#(?.*))?$/.exec(urlString) - if (!match) { - throw new Error(`Invalid URL: ${urlString}`) - } + if (!match) { + throw new Error(`Invalid URL: ${urlString}`) + } - let {type, data, hash} = match.groups as any - const mediaType = type.split(';') - hash = stripHash ? '' : hash + let {type, data, hash} = match.groups as any + const mediaType = type.split(";") + hash = stripHash ? "" : hash - let isBase64 = false - if (mediaType[mediaType.length - 1] === 'base64') { - mediaType.pop() - isBase64 = true - } + let isBase64 = false + if (mediaType[mediaType.length - 1] === "base64") { + mediaType.pop() + isBase64 = true + } - // Lowercase MIME type - const mimeType = mediaType.shift()?.toLowerCase() ?? '' - const attributes = mediaType - .map((attribute: string) => { - let [key, value = ''] = attribute.split('=').map((s: string) => s.trim()) + // Lowercase MIME type + const mimeType = mediaType.shift()?.toLowerCase() ?? "" + const attributes = mediaType + .map((attribute: string) => { + let [key, value = ""] = attribute.split("=").map((s: string) => s.trim()) - // Lowercase `charset` - if (key === 'charset') { - value = value.toLowerCase() + // Lowercase `charset` + if (key === "charset") { + value = value.toLowerCase() - if (value === DATA_URL_DEFAULT_CHARSET) { - return '' - } - } + if (value === DATA_URL_DEFAULT_CHARSET) { + return "" + } + } - return `${key}${value ? `=${value}` : ''}` - }) - .filter(Boolean) + return `${key}${value ? `=${value}` : ""}` + }) + .filter(Boolean) - const normalizedMediaType = [ - ...attributes, - ] + const normalizedMediaType = [...attributes] - if (isBase64) { - normalizedMediaType.push('base64') - } + if (isBase64) { + normalizedMediaType.push("base64") + } - if (normalizedMediaType.length > 0 || (mimeType && mimeType !== DATA_URL_DEFAULT_MIME_TYPE)) { - normalizedMediaType.unshift(mimeType) - } + if (normalizedMediaType.length > 0 || (mimeType && mimeType !== DATA_URL_DEFAULT_MIME_TYPE)) { + normalizedMediaType.unshift(mimeType) + } - return `data:${normalizedMediaType.join(';')},${isBase64 ? data.trim() : data}${hash ? `#${hash}` : ''}` + return `data:${normalizedMediaType.join(";")},${isBase64 ? data.trim() : data}${hash ? `#${hash}` : ""}` } /** @@ -374,212 +370,224 @@ normalizeUrl('//www.sindresorhus.com:80/../baz?b=bar&a=foo'); */ export default function normalizeUrl(urlString: string, opts?: Options): string { - const options = { - defaultProtocol: 'http', - normalizeProtocol: true, - forceHttp: false, - forceHttps: false, - stripAuthentication: true, - stripHash: false, - stripTextFragment: true, - stripWWW: true, - removeQueryParameters: [/^utm_\w+/i], - removeTrailingSlash: true, - removeSingleSlash: true, - removeDirectoryIndex: false, - removeExplicitPort: false, - sortQueryParameters: true, - ...opts, - } + const options = { + defaultProtocol: "http", + normalizeProtocol: true, + forceHttp: false, + forceHttps: false, + stripAuthentication: true, + stripHash: false, + stripTextFragment: true, + stripWWW: true, + removeQueryParameters: [/^utm_\w+/i], + removeTrailingSlash: true, + removeSingleSlash: true, + removeDirectoryIndex: false, + removeExplicitPort: false, + sortQueryParameters: true, + ...opts, + } - // Legacy: Append `:` to the protocol if missing. - if (typeof options.defaultProtocol === 'string' && !options.defaultProtocol.endsWith(':')) { - options.defaultProtocol = `${options.defaultProtocol}:` - } + // Legacy: Append `:` to the protocol if missing. + if (typeof options.defaultProtocol === "string" && !options.defaultProtocol.endsWith(":")) { + options.defaultProtocol = `${options.defaultProtocol}:` + } - urlString = urlString.trim() + urlString = urlString.trim() - // Data URL - if (/^data:/i.test(urlString)) { - return normalizeDataURL(urlString, options) - } + // Data URL + if (/^data:/i.test(urlString)) { + return normalizeDataURL(urlString, options) + } - if (hasCustomProtocol(urlString)) { - return urlString - } + if (hasCustomProtocol(urlString)) { + return urlString + } - const hasRelativeProtocol = urlString.startsWith('//') - const isRelativeUrl = !hasRelativeProtocol && /^\.*\//.test(urlString) + const hasRelativeProtocol = urlString.startsWith("//") + const isRelativeUrl = !hasRelativeProtocol && /^\.*\//.test(urlString) - // Prepend protocol - if (!isRelativeUrl) { - urlString = urlString.replace(/^(?!(?:\w+:)?\/\/)|^\/\//, options.defaultProtocol) - } + // Prepend protocol + if (!isRelativeUrl) { + urlString = urlString.replace(/^(?!(?:\w+:)?\/\/)|^\/\//, options.defaultProtocol) + } - const urlObject = new URL(urlString) + const urlObject = new URL(urlString) - if (options.forceHttp && options.forceHttps) { - throw new Error('The `forceHttp` and `forceHttps` options cannot be used together') - } + if (options.forceHttp && options.forceHttps) { + throw new Error("The `forceHttp` and `forceHttps` options cannot be used together") + } - if (options.forceHttp && urlObject.protocol === 'https:') { - urlObject.protocol = 'http:' - } + if (options.forceHttp && urlObject.protocol === "https:") { + urlObject.protocol = "http:" + } - if (options.forceHttps && urlObject.protocol === 'http:') { - urlObject.protocol = 'https:' - } + if (options.forceHttps && urlObject.protocol === "http:") { + urlObject.protocol = "https:" + } - // Remove auth - if (options.stripAuthentication) { - urlObject.username = '' - urlObject.password = '' - } + // Remove auth + if (options.stripAuthentication) { + urlObject.username = "" + urlObject.password = "" + } - // Remove hash - if (options.stripHash) { - urlObject.hash = '' - } else if (options.stripTextFragment) { - urlObject.hash = urlObject.hash.replace(/#?:~:text.*?$/i, '') - } + // Remove hash + if (options.stripHash) { + urlObject.hash = "" + } else if (options.stripTextFragment) { + urlObject.hash = urlObject.hash.replace(/#?:~:text.*?$/i, "") + } - // Remove duplicate slashes if not preceded by a protocol - // NOTE: This could be implemented using a single negative lookbehind - // regex, but we avoid that to maintain compatibility with older js engines - // which do not have support for that feature. - if (urlObject.pathname) { - // TODO: Replace everything below with `urlObject.pathname = urlObject.pathname.replace(/(? 0) { - let pathComponents = urlObject.pathname.split('/') - const lastComponent = pathComponents[pathComponents.length - 1] + if (Array.isArray(options.removeDirectoryIndex) && options.removeDirectoryIndex.length > 0) { + let pathComponents = urlObject.pathname.split("/") + const lastComponent = pathComponents[pathComponents.length - 1] - if (testParameter(lastComponent, options.removeDirectoryIndex)) { - pathComponents = pathComponents.slice(0, -1) - urlObject.pathname = pathComponents.slice(1).join('/') + '/' - } - } + if (testParameter(lastComponent, options.removeDirectoryIndex)) { + pathComponents = pathComponents.slice(0, -1) + urlObject.pathname = pathComponents.slice(1).join("/") + "/" + } + } - if (urlObject.hostname) { - // Remove trailing dot - urlObject.hostname = urlObject.hostname.replace(/\.$/, '') + if (urlObject.hostname) { + // Remove trailing dot + urlObject.hostname = urlObject.hostname.replace(/\.$/, "") - // Remove `www.` - if (options.stripWWW && /^www\.(?!www\.)[a-z\-\d]{1,63}\.[a-z.\-\d]{2,63}$/.test(urlObject.hostname)) { - // Each label should be max 63 at length (min: 1). - // Source: https://en.wikipedia.org/wiki/Hostname#Restrictions_on_valid_host_names - // Each TLD should be up to 63 characters long (min: 2). - // It is technically possible to have a single character TLD, but none currently exist. - urlObject.hostname = urlObject.hostname.replace(/^www\./, '') - } - } + // Remove `www.` + if ( + options.stripWWW && + /^www\.(?!www\.)[a-z\-\d]{1,63}\.[a-z.\-\d]{2,63}$/.test(urlObject.hostname) + ) { + // Each label should be max 63 at length (min: 1). + // Source: https://en.wikipedia.org/wiki/Hostname#Restrictions_on_valid_host_names + // Each TLD should be up to 63 characters long (min: 2). + // It is technically possible to have a single character TLD, but none currently exist. + urlObject.hostname = urlObject.hostname.replace(/^www\./, "") + } + } - // Remove query unwanted parameters - if (Array.isArray(options.removeQueryParameters)) { - // @ts-ignore - for (const key of [...urlObject.searchParams.keys()]) { - if (testParameter(key, options.removeQueryParameters)) { - urlObject.searchParams.delete(key) - } - } - } + // Remove query unwanted parameters + if (Array.isArray(options.removeQueryParameters)) { + // @ts-ignore + for (const key of [...urlObject.searchParams.keys()]) { + if (testParameter(key, options.removeQueryParameters)) { + urlObject.searchParams.delete(key) + } + } + } - if (!Array.isArray(options.keepQueryParameters) && options.removeQueryParameters === true) { - urlObject.search = '' - } + if (!Array.isArray(options.keepQueryParameters) && options.removeQueryParameters === true) { + urlObject.search = "" + } - // Keep wanted query parameters - if (Array.isArray(options.keepQueryParameters) && options.keepQueryParameters.length > 0) { - // @ts-ignore - for (const key of [...urlObject.searchParams.keys()]) { - if (!testParameter(key, options.keepQueryParameters)) { - urlObject.searchParams.delete(key) - } - } - } + // Keep wanted query parameters + if (Array.isArray(options.keepQueryParameters) && options.keepQueryParameters.length > 0) { + // @ts-ignore + for (const key of [...urlObject.searchParams.keys()]) { + if (!testParameter(key, options.keepQueryParameters)) { + urlObject.searchParams.delete(key) + } + } + } - // Sort query parameters - if (options.sortQueryParameters) { - urlObject.searchParams.sort() + // Sort query parameters + if (options.sortQueryParameters) { + urlObject.searchParams.sort() - // Calling `.sort()` encodes the search parameters, so we need to decode them again. - try { - urlObject.search = decodeURIComponent(urlObject.search) - } catch {} - } + // Calling `.sort()` encodes the search parameters, so we need to decode them again. + try { + urlObject.search = decodeURIComponent(urlObject.search) + } catch {} + } - if (options.removeTrailingSlash) { - urlObject.pathname = urlObject.pathname.replace(/\/$/, '') - } + if (options.removeTrailingSlash) { + urlObject.pathname = urlObject.pathname.replace(/\/$/, "") + } - // Remove an explicit port number, excluding a default port number, if applicable - if (options.removeExplicitPort && urlObject.port) { - urlObject.port = '' - } + // Remove an explicit port number, excluding a default port number, if applicable + if (options.removeExplicitPort && urlObject.port) { + urlObject.port = "" + } - const oldUrlString = urlString + const oldUrlString = urlString - // Take advantage of many of the Node `url` normalizations - urlString = urlObject.toString() + // Take advantage of many of the Node `url` normalizations + urlString = urlObject.toString() - if (!options.removeSingleSlash && urlObject.pathname === '/' && !oldUrlString.endsWith('/') && urlObject.hash === '') { - urlString = urlString.replace(/\/$/, '') - } + if ( + !options.removeSingleSlash && + urlObject.pathname === "/" && + !oldUrlString.endsWith("/") && + urlObject.hash === "" + ) { + urlString = urlString.replace(/\/$/, "") + } - // Remove ending `/` unless removeSingleSlash is false - if ((options.removeTrailingSlash || urlObject.pathname === '/') && urlObject.hash === '' && options.removeSingleSlash) { - urlString = urlString.replace(/\/$/, '') - } + // Remove ending `/` unless removeSingleSlash is false + if ( + (options.removeTrailingSlash || urlObject.pathname === "/") && + urlObject.hash === "" && + options.removeSingleSlash + ) { + urlString = urlString.replace(/\/$/, "") + } - // Restore relative protocol, if applicable - if (hasRelativeProtocol && !options.normalizeProtocol) { - urlString = urlString.replace(/^http:\/\//, '//') - } + // Restore relative protocol, if applicable + if (hasRelativeProtocol && !options.normalizeProtocol) { + urlString = urlString.replace(/^http:\/\//, "//") + } - // Remove http/https - if (options.stripProtocol) { - urlString = urlString.replace(/^(?:https?:)?\/\//, '') - } + // Remove http/https + if (options.stripProtocol) { + urlString = urlString.replace(/^(?:https?:)?\/\//, "") + } - return urlString + return urlString } diff --git a/packages/net/__tests__/adapter.test.ts b/packages/net/__tests__/adapter.test.ts index dc37e78..1ccf2f1 100644 --- a/packages/net/__tests__/adapter.test.ts +++ b/packages/net/__tests__/adapter.test.ts @@ -1,13 +1,12 @@ import EventEmitter from "events" -import { describe, expect, it, vi, beforeEach, afterEach } from "vitest" -import { isRelayUrl } from "@welshman/util" -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" +import {describe, expect, it, vi, beforeEach, afterEach} from "vitest" +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', () => { +vi.mock("isomorphic-ws", () => { const WebSocket = vi.fn(function (this: any) { setTimeout(() => this.onopen()) }) @@ -18,7 +17,7 @@ vi.mock('isomorphic-ws', () => { this.onclose() }) - return { default: WebSocket } + return {default: WebSocket} }) describe("SocketAdapter", () => { @@ -27,7 +26,7 @@ describe("SocketAdapter", () => { beforeEach(() => { vi.useFakeTimers() - socket = new Socket('wss://test.relay') + socket = new Socket("wss://test.relay") adapter = new SocketAdapter(socket) }) @@ -48,15 +47,15 @@ describe("SocketAdapter", () => { const receiveSpy = vi.fn() adapter.on(AdapterEvent.Receive, receiveSpy) - const message: RelayMessage = ["EVENT", "123", { id: "123", kind: 1 }] + const message: RelayMessage = ["EVENT", "123", {id: "123", kind: 1}] socket.emit(SocketEvent.Receive, message, "wss://test.relay") expect(receiveSpy).toHaveBeenCalledWith(message, "wss://test.relay") }) it("should send messages to socket", () => { - const sendSpy = vi.spyOn(socket, 'send') - const message: ClientMessage = ["EVENT", { id: "123", kind: 1 }] + const sendSpy = vi.spyOn(socket, "send") + const message: ClientMessage = ["EVENT", {id: "123", kind: 1}] adapter.send(message) expect(sendSpy).toHaveBeenCalledWith(message) @@ -77,7 +76,7 @@ describe("LocalAdapter", () => { const mockRelay = new EventEmitter() Object.assign(mockRelay, { send: vi.fn(), - removeAllListeners: vi.fn() + removeAllListeners: vi.fn(), }) relay = mockRelay as unknown as LocalRelay & EventEmitter adapter = new LocalAdapter(relay) @@ -98,14 +97,14 @@ describe("LocalAdapter", () => { const receiveSpy = vi.fn() adapter.on(AdapterEvent.Receive, receiveSpy) - const message: RelayMessage = ["EVENT", "123", { id: "123", kind: 1 }] + const message: RelayMessage = ["EVENT", "123", {id: "123", kind: 1}] relay.emit("*", ...message) expect(receiveSpy).toHaveBeenCalledWith(message, LOCAL_RELAY_URL) }) it("should send messages to relay", () => { - const message: ClientMessage = ["EVENT", { id: "123", kind: 1 }] + const message: ClientMessage = ["EVENT", {id: "123", kind: 1}] adapter.send(message) expect(relay.send).toHaveBeenCalledWith("EVENT", message[1]) @@ -136,13 +135,13 @@ describe("getAdapter", () => { it("should return LocalAdapter for local relay URL", () => { const url = LOCAL_RELAY_URL - const adapter = getAdapter(url, { repository }) + const adapter = getAdapter(url, {repository}) expect(adapter).toBeInstanceOf(LocalAdapter) }) it("should return SocketAdapter for remote relay URL", () => { const url = "wss://test.relay" - const adapter = getAdapter(url, { pool }) + const adapter = getAdapter(url, {pool}) expect(adapter).toBeInstanceOf(SocketAdapter) }) @@ -151,9 +150,12 @@ describe("getAdapter", () => { const getCustomAdapter = vi.fn().mockReturnValue(customAdapter) const url = "wss://test.relay" - const adapter = getAdapter(url, { getAdapter: getCustomAdapter }) + const adapter = getAdapter(url, {getAdapter: getCustomAdapter}) - expect(getCustomAdapter).toHaveBeenCalledWith(url, expect.objectContaining({ getAdapter: getCustomAdapter })) + expect(getCustomAdapter).toHaveBeenCalledWith( + url, + expect.objectContaining({getAdapter: getCustomAdapter}), + ) expect(adapter).toBe(customAdapter) }) }) diff --git a/packages/net/__tests__/auth.test.ts b/packages/net/__tests__/auth.test.ts index c5745af..d773504 100644 --- a/packages/net/__tests__/auth.test.ts +++ b/packages/net/__tests__/auth.test.ts @@ -1,12 +1,11 @@ -import { describe, expect, it, vi, beforeEach, afterEach } from "vitest" -import { Socket, SocketStatus, SocketEvent } from "../src/socket" -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" -import { RelayMessage } from "../src/message" +import {describe, expect, it, vi, beforeEach, afterEach} from "vitest" +import {Socket, SocketStatus, SocketEvent} from "../src/socket" +import {StampedEvent, CLIENT_AUTH} from "@welshman/util" +import {Nip01Signer} from "@welshman/signer" +import {AuthStatus, AuthStateEvent} from "../src/auth" +import {RelayMessage} from "../src/message" -vi.mock('isomorphic-ws', () => { +vi.mock("isomorphic-ws", () => { const WebSocket = vi.fn(function (this: any) { setTimeout(() => this.onopen()) }) @@ -17,14 +16,14 @@ vi.mock('isomorphic-ws', () => { this.onclose() }) - return { default: WebSocket } + return {default: WebSocket} }) -describe('auth', () => { +describe("auth", () => { let socket: Socket beforeEach(() => { - socket = new Socket('wss://test.relay') + socket = new Socket("wss://test.relay") }) afterEach(() => { @@ -72,7 +71,7 @@ describe('auth', () => { }) it("should handle client AUTH message", () => { - const message: RelayMessage = ["AUTH", { id: "123", kind: CLIENT_AUTH }] + const message: RelayMessage = ["AUTH", {id: "123", kind: CLIENT_AUTH}] socket.emit(SocketEvent.Sending, message) expect(socket.auth.status).toBe(AuthStatus.PendingResponse) @@ -113,7 +112,7 @@ describe('auth', () => { const sign = vi.fn() await expect(socket.auth.authenticate(sign)).rejects.toThrow( - "Attempted to authenticate with no challenge" + "Attempted to authenticate with no challenge", ) }) @@ -124,7 +123,7 @@ describe('auth', () => { socket.auth.status = AuthStatus.PendingResponse await expect(socket.auth.authenticate(sign)).rejects.toThrow( - "Attempted to authenticate when auth is already auth:status:pending_response" + "Attempted to authenticate when auth is already auth:status:pending_response", ) }) @@ -140,7 +139,7 @@ describe('auth', () => { }) it("should send AUTH message", async () => { - const sendSpy = vi.spyOn(socket, 'send') + const sendSpy = vi.spyOn(socket, "send") let event socket.auth.challenge = "challenge123" diff --git a/packages/net/__tests__/policy.test.ts b/packages/net/__tests__/policy.test.ts index 282c949..45353d3 100644 --- a/packages/net/__tests__/policy.test.ts +++ b/packages/net/__tests__/policy.test.ts @@ -1,14 +1,14 @@ -import { AUTH_JOIN } from "@welshman/util" -import { describe, expect, it, vi, beforeEach, afterEach } from "vitest" -import { Socket, SocketStatus, SocketEvent } from "../src/socket" -import { AuthStatus, AuthStateEvent } from "../src/auth" +import {AUTH_JOIN} from "@welshman/util" +import {describe, expect, it, vi, beforeEach, afterEach} from "vitest" +import {Socket, SocketStatus, SocketEvent} from "../src/socket" +import {AuthStatus, AuthStateEvent} from "../src/auth" import { socketPolicyAuthBuffer, socketPolicyConnectOnSend, socketPolicyCloseOnTimeout, - socketPolicyReopenActive + socketPolicyReopenActive, } from "../src/policy" -import { ClientMessage, RelayMessage } from "../src/message" +import {ClientMessage, RelayMessage} from "../src/message" // Hoist mock definition to top level const mockWs = vi.hoisted(() => ({ @@ -21,11 +21,11 @@ const mockWs = vi.hoisted(() => ({ })) // Mock the WebSocket module -vi.mock('isomorphic-ws', () => ({ - default: mockWs +vi.mock("isomorphic-ws", () => ({ + default: mockWs, })) -describe('policy', () => { +describe("policy", () => { let socket: Socket beforeEach(() => { @@ -42,22 +42,22 @@ describe('policy', () => { describe("socketPolicyAuthBuffer", () => { it("should buffer messages when not authenticated", () => { const cleanup = socketPolicyAuthBuffer(socket) - const sendSpy = vi.spyOn(socket, 'send') + const sendSpy = vi.spyOn(socket, "send") socket.emit(SocketEvent.Receive, ["AUTH", "challenge"]) // Regular event should be buffered - const event: ClientMessage = ["EVENT", { id: "123"}] + const event: ClientMessage = ["EVENT", {id: "123"}] socket.send(event) expect(sendSpy).toHaveBeenCalledWith(event) // Auth event should not be buffered - const authEvent: ClientMessage = ["AUTH", { id: "456" }] + const authEvent: ClientMessage = ["AUTH", {id: "456"}] socket.send(authEvent) expect(sendSpy).toHaveBeenCalledWith(authEvent) // Auth join event should not be buffered - const joinEvent: ClientMessage = ["EVENT", { id: "789", kind: AUTH_JOIN }] + const joinEvent: ClientMessage = ["EVENT", {id: "789", kind: AUTH_JOIN}] socket.send(joinEvent) expect(sendSpy).toHaveBeenCalledWith(joinEvent) @@ -66,18 +66,18 @@ describe('policy', () => { it("should send buffered messages when auth succeeds", () => { const cleanup = socketPolicyAuthBuffer(socket) - const sendSpy = vi.spyOn(socket, 'send') + const sendSpy = vi.spyOn(socket, "send") socket.emit(SocketEvent.Receive, ["AUTH", "challenge"]) // Buffer some messages - const event1: ClientMessage = ["EVENT", { id: "123"}] - const event2: ClientMessage = ["EVENT", { id: "456"}] + const event1: ClientMessage = ["EVENT", {id: "123"}] + const event2: ClientMessage = ["EVENT", {id: "456"}] socket.send(event1) socket.send(event2) // Auth succeeds - socket.send(["AUTH", { id: "auth" }]) + socket.send(["AUTH", {id: "auth"}]) socket.emit(AuthStateEvent.Status, AuthStatus.Ok) expect(sendSpy).toHaveBeenCalledWith(event1) @@ -88,12 +88,12 @@ describe('policy', () => { it("should handle CLOSE messages properly", () => { const cleanup = socketPolicyAuthBuffer(socket) - const sendSpy = vi.spyOn(socket, 'send') + const sendSpy = vi.spyOn(socket, "send") socket.emit(SocketEvent.Receive, ["AUTH", "challenge"]) // Buffer a REQ message - const req: ClientMessage = ["REQ", "123", { kinds: [1] }] + const req: ClientMessage = ["REQ", "123", {kinds: [1]}] socket.send(req) // Send CLOSE for buffered REQ @@ -109,10 +109,13 @@ describe('policy', () => { it("should retry events once when auth-required", () => { const cleanup = socketPolicyAuthBuffer(socket) - const recvQueueRemoveSpy = vi.spyOn(socket._recvQueue, 'remove') + const recvQueueRemoveSpy = vi.spyOn(socket._recvQueue, "remove") // Send an event - const event: ClientMessage = ["EVENT", { id: "123", kind: 1, content: "", tags: [], pubkey: "", sig: "" }] + const event: ClientMessage = [ + "EVENT", + {id: "123", kind: 1, content: "", tags: [], pubkey: "", sig: ""}, + ] socket.emit(SocketEvent.Send, event) // Receive auth-required rejection @@ -134,10 +137,10 @@ describe('policy', () => { it("should retry REQ once when auth-required", () => { const cleanup = socketPolicyAuthBuffer(socket) - const recvQueueRemoveSpy = vi.spyOn(socket._recvQueue, 'remove') + const recvQueueRemoveSpy = vi.spyOn(socket._recvQueue, "remove") // Send a REQ - const req: ClientMessage = ["REQ", "123", { kinds: [1] }] + const req: ClientMessage = ["REQ", "123", {kinds: [1]}] socket.emit(SocketEvent.Send, req) // Receive auth-required rejection @@ -159,10 +162,13 @@ describe('policy', () => { it("should not retry AUTH_JOIN events", () => { const cleanup = socketPolicyAuthBuffer(socket) - const sendSpy = vi.spyOn(socket, 'send') + const sendSpy = vi.spyOn(socket, "send") // Send an AUTH_JOIN event - const event: ClientMessage = ["EVENT", { id: "123", kind: AUTH_JOIN, content: "", tags: [], pubkey: "", sig: "" }] + const event: ClientMessage = [ + "EVENT", + {id: "123", kind: AUTH_JOIN, content: "", tags: [], pubkey: "", sig: ""}, + ] socket.emit(SocketEvent.Send, event) // Receive auth-required rejection @@ -176,10 +182,13 @@ describe('policy', () => { it("should clear pending messages on successful response", () => { const cleanup = socketPolicyAuthBuffer(socket) - const sendSpy = vi.spyOn(socket, 'send') + const sendSpy = vi.spyOn(socket, "send") // Send an event - const event: ClientMessage = ["EVENT", { id: "123", kind: 1, content: "", tags: [], pubkey: "", sig: "" }] + const event: ClientMessage = [ + "EVENT", + {id: "123", kind: 1, content: "", tags: [], pubkey: "", sig: ""}, + ] socket.emit(SocketEvent.Send, event) // Receive successful response @@ -198,13 +207,13 @@ describe('policy', () => { describe("socketPolicyConnectOnSend", () => { it("should open socket on send when closed", () => { const cleanup = socketPolicyConnectOnSend(socket) - const openSpy = vi.spyOn(socket, 'open') + const openSpy = vi.spyOn(socket, "open") // Socket starts closed socket.emit(SocketEvent.Status, SocketStatus.Closed) // Send a message - const event: ClientMessage = ["EVENT", { id: "123", kind: 1 }] + const event: ClientMessage = ["EVENT", {id: "123", kind: 1}] socket.emit(SocketEvent.Sending, event) // Should open the socket @@ -215,13 +224,13 @@ describe('policy', () => { it("should not open socket if already open", () => { const cleanup = socketPolicyConnectOnSend(socket) - const openSpy = vi.spyOn(socket, 'open') + const openSpy = vi.spyOn(socket, "open") // Socket is open socket.emit(SocketEvent.Status, SocketStatus.Open) // Send a message - const event: ClientMessage = ["EVENT", { id: "123", kind: 1 }] + const event: ClientMessage = ["EVENT", {id: "123", kind: 1}] socket.emit(SocketEvent.Sending, event) // Should not try to open the socket @@ -232,14 +241,14 @@ describe('policy', () => { it("should not open socket if there was a recent error", () => { const cleanup = socketPolicyConnectOnSend(socket) - const openSpy = vi.spyOn(socket, 'open') + const openSpy = vi.spyOn(socket, "open") // Socket has an error socket.emit(SocketEvent.Status, SocketStatus.Error) socket.emit(SocketEvent.Status, SocketStatus.Closed) // Send a message - const event: ClientMessage = ["EVENT", { id: "123", kind: 1 }] + const event: ClientMessage = ["EVENT", {id: "123", kind: 1}] socket.emit(SocketEvent.Sending, event) // Should not try to open the socket due to recent error @@ -261,7 +270,7 @@ describe('policy', () => { describe("socketPolicyCloseOnTimeout", () => { it("should close socket after 30 seconds of inactivity", async () => { const cleanup = socketPolicyCloseOnTimeout(socket) - const closeSpy = vi.spyOn(socket, 'close') + const closeSpy = vi.spyOn(socket, "close") // Set socket as open socket.emit(SocketEvent.Status, SocketStatus.Open) @@ -277,7 +286,7 @@ describe('policy', () => { it("should reset timer on send activity", () => { const cleanup = socketPolicyCloseOnTimeout(socket) - const closeSpy = vi.spyOn(socket, 'close') + const closeSpy = vi.spyOn(socket, "close") // Set socket as open socket.emit(SocketEvent.Status, SocketStatus.Open) @@ -286,7 +295,7 @@ describe('policy', () => { vi.advanceTimersByTime(20000) // Send a message - socket.emit(SocketEvent.Send, ["EVENT", { id: "123" }]) + socket.emit(SocketEvent.Send, ["EVENT", {id: "123"}]) // Advance time partially again vi.advanceTimersByTime(20000) @@ -305,7 +314,7 @@ describe('policy', () => { it("should reset timer on receive activity", () => { const cleanup = socketPolicyCloseOnTimeout(socket) - const closeSpy = vi.spyOn(socket, 'close') + const closeSpy = vi.spyOn(socket, "close") // Set socket as open socket.emit(SocketEvent.Status, SocketStatus.Open) @@ -314,7 +323,7 @@ describe('policy', () => { vi.advanceTimersByTime(20000) // Receive a message - socket.emit(SocketEvent.Receive, ["EVENT", "123", { id: "123" }]) + socket.emit(SocketEvent.Receive, ["EVENT", "123", {id: "123"}]) // Advance time partially again vi.advanceTimersByTime(20000) @@ -333,7 +342,7 @@ describe('policy', () => { it("should not close socket if not open", () => { const cleanup = socketPolicyCloseOnTimeout(socket) - const closeSpy = vi.spyOn(socket, 'close') + const closeSpy = vi.spyOn(socket, "close") // Set socket as closed socket.emit(SocketEvent.Status, SocketStatus.Closed) @@ -351,10 +360,10 @@ describe('policy', () => { describe("socketPolicyReopenActive", () => { it("should reopen socket when closed with pending messages", async () => { const cleanup = socketPolicyReopenActive(socket) - const sendSpy = vi.spyOn(socket, 'send') + const sendSpy = vi.spyOn(socket, "send") // Send an event that will be pending - const event: ClientMessage = ["EVENT", { id: "123", kind: 1 }] + const event: ClientMessage = ["EVENT", {id: "123", kind: 1}] socket.emit(SocketEvent.Send, event) // Socket closes @@ -371,10 +380,10 @@ describe('policy', () => { it("should reopen socket when closed with pending requests", async () => { const cleanup = socketPolicyReopenActive(socket) - const sendSpy = vi.spyOn(socket, 'send') + const sendSpy = vi.spyOn(socket, "send") // Send a request that will be pending - const req: ClientMessage = ["REQ", "123", { kinds: [1] }] + const req: ClientMessage = ["REQ", "123", {kinds: [1]}] socket.emit(SocketEvent.Send, req) // Socket closes @@ -391,10 +400,10 @@ describe('policy', () => { it("should not reopen socket immediately after previous open", async () => { const cleanup = socketPolicyReopenActive(socket) - const sendSpy = vi.spyOn(socket, 'send') + const sendSpy = vi.spyOn(socket, "send") // Send an event that will be pending - const event: ClientMessage = ["EVENT", { id: "123", kind: 1 }] + const event: ClientMessage = ["EVENT", {id: "123", kind: 1}] socket.emit(SocketEvent.Send, event) // Socket opens then closes quickly @@ -418,10 +427,10 @@ describe('policy', () => { it("should remove pending messages when they complete", () => { const cleanup = socketPolicyReopenActive(socket) - const sendSpy = vi.spyOn(socket, 'send') + const sendSpy = vi.spyOn(socket, "send") // Send an event that will be pending - const event: ClientMessage = ["EVENT", { id: "123", kind: 1 }] + const event: ClientMessage = ["EVENT", {id: "123", kind: 1}] socket.emit(SocketEvent.Send, event) // Event completes successfully @@ -441,10 +450,10 @@ describe('policy', () => { it("should remove pending messages when closed", () => { const cleanup = socketPolicyReopenActive(socket) - const sendSpy = vi.spyOn(socket, 'send') + const sendSpy = vi.spyOn(socket, "send") // Send a request that will be pending - const req: ClientMessage = ["REQ", "123", { kinds: [1] }] + const req: ClientMessage = ["REQ", "123", {kinds: [1]}] socket.emit(SocketEvent.Send, req) // Send close for the request diff --git a/packages/net/__tests__/pool.test.ts b/packages/net/__tests__/pool.test.ts index 7c2793e..0273273 100644 --- a/packages/net/__tests__/pool.test.ts +++ b/packages/net/__tests__/pool.test.ts @@ -1,9 +1,8 @@ -import { describe, expect, it, vi, beforeEach, afterEach } from "vitest" -import { Socket } from "../src/socket" -import { Pool, makeSocket } from "../src/pool" -import { normalizeRelayUrl } from "@welshman/util" +import {describe, expect, it, vi, beforeEach, afterEach} from "vitest" +import {Socket} from "../src/socket" +import {Pool} from "../src/pool" -vi.mock('isomorphic-ws', () => { +vi.mock("isomorphic-ws", () => { const WebSocket = vi.fn(function (this: any) { setTimeout(() => this.onopen()) }) @@ -14,7 +13,7 @@ vi.mock('isomorphic-ws', () => { this.onclose() }) - return { default: WebSocket } + return {default: WebSocket} }) describe("Pool", () => { @@ -95,7 +94,7 @@ describe("Pool", () => { describe("remove", () => { 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 as unknown as Socket) pool.remove(mockSocket.url) @@ -113,7 +112,7 @@ describe("Pool", () => { describe("clear", () => { it("should remove all sockets", () => { const urls = ["wss://test1.relay", "wss://test2.relay"] - const mockSockets = urls.map(url => ({ url, cleanup: vi.fn() })) + const mockSockets = urls.map(url => ({url, cleanup: vi.fn()})) for (const mockSocket of mockSockets) { pool._data.set(mockSocket.url, mockSocket as unknown as Socket) diff --git a/packages/net/__tests__/publish.test.ts b/packages/net/__tests__/publish.test.ts index 5a67ca8..e2f4e0e 100644 --- a/packages/net/__tests__/publish.test.ts +++ b/packages/net/__tests__/publish.test.ts @@ -1,10 +1,9 @@ -import { describe, expect, it, vi, beforeEach, afterEach } from "vitest" -import { EventEmitter } from "events" -import { SinglePublish, MultiPublish, PublishEvent, PublishStatus, } from "../src/publish" -import { AbstractAdapter, AdapterEvent, MockAdapter } from "../src/adapter" -import { ClientMessageType, RelayMessage } from "../src/message" -import { SignedEvent, makeEvent } from "@welshman/util" -import { Nip01Signer } from '@welshman/signer' +import {describe, expect, it, vi, beforeEach, afterEach} from "vitest" +import {SinglePublish, MultiPublish, PublishEvent} from "../src/publish" +import {MockAdapter} from "../src/adapter" +import {ClientMessageType} from "../src/message" +import {makeEvent} from "@welshman/util" +import {Nip01Signer} from "@welshman/signer" describe("SinglePublish", () => { beforeEach(() => { @@ -17,12 +16,12 @@ describe("SinglePublish", () => { it("success works", async () => { const sendSpy = vi.fn() - const adapter = new MockAdapter('1', sendSpy) + const adapter = new MockAdapter("1", sendSpy) const signer = Nip01Signer.ephemeral() const event = await signer.sign(makeEvent(1)) const pub = new SinglePublish({ - relay: '1', + relay: "1", context: {getAdapter: () => adapter}, event, }) @@ -50,12 +49,12 @@ describe("SinglePublish", () => { it("failure works", async () => { const sendSpy = vi.fn() - const adapter = new MockAdapter('1', sendSpy) + const adapter = new MockAdapter("1", sendSpy) const signer = Nip01Signer.ephemeral() const event = await signer.sign(makeEvent(1)) const pub = new SinglePublish({ - relay: '1', + relay: "1", context: {getAdapter: () => adapter}, event, }) @@ -83,12 +82,12 @@ describe("SinglePublish", () => { it("timeout works", async () => { const sendSpy = vi.fn() - const adapter = new MockAdapter('1', sendSpy) + const adapter = new MockAdapter("1", sendSpy) const signer = Nip01Signer.ephemeral() const event = await signer.sign(makeEvent(1)) const pub = new SinglePublish({ - relay: '1', + relay: "1", context: {getAdapter: () => adapter}, event, }) @@ -117,12 +116,12 @@ describe("SinglePublish", () => { it("abort works", async () => { const sendSpy = vi.fn() - const adapter = new MockAdapter('1', sendSpy) + const adapter = new MockAdapter("1", sendSpy) const signer = Nip01Signer.ephemeral() const event = await signer.sign(makeEvent(1)) const pub = new SinglePublish({ - relay: '1', + relay: "1", context: {getAdapter: () => adapter}, event, }) @@ -163,27 +162,31 @@ describe("MultiPublish", () => { it("should all basically work", async () => { const send1Spy = vi.fn() - const adapter1 = new MockAdapter('1', send1Spy) + const adapter1 = new MockAdapter("1", send1Spy) const send2Spy = vi.fn() - const adapter2 = new MockAdapter('2', send2Spy) + const adapter2 = new MockAdapter("2", send2Spy) const send3Spy = vi.fn() - const adapter3 = new MockAdapter('3', send3Spy) + const adapter3 = new MockAdapter("3", send3Spy) const signer = Nip01Signer.ephemeral() const event = await signer.sign(makeEvent(1)) const pub = new MultiPublish({ event, - relays: ['1', '2', '3'], + relays: ["1", "2", "3"], context: { getAdapter: (url: string) => { - switch(url) { - case '1': return adapter1 - case '2': return adapter2 - case '3': return adapter3 - default: throw new Error(`Unknown relay: ${url}`) + switch (url) { + case "1": + return adapter1 + case "2": + return adapter2 + case "3": + return adapter3 + default: + throw new Error(`Unknown relay: ${url}`) } }, - } + }, }) const successSpy = vi.fn() @@ -199,7 +202,6 @@ describe("MultiPublish", () => { adapter1.receive(["OK", event.id, true, "hi"]) adapter2.receive(["OK", event.id, false, "hi"]) - await vi.runAllTimers() expect(successSpy).toHaveBeenCalledWith(event.id, "hi", "1") diff --git a/packages/net/__tests__/request.test.ts b/packages/net/__tests__/request.test.ts index d87fbb9..20eb722 100644 --- a/packages/net/__tests__/request.test.ts +++ b/packages/net/__tests__/request.test.ts @@ -1,9 +1,9 @@ -import { describe, expect, it, vi, beforeEach, afterEach } from "vitest" -import { Nip01Signer } from '@welshman/signer' -import { makeEvent } from '@welshman/util' -import { ClientMessageType } from "../src/message" -import { MockAdapter } from "../src/adapter" -import { SingleRequest, MultiRequest, RequestEvent } from "../src/request" +import {describe, expect, it, vi, beforeEach, afterEach} from "vitest" +import {Nip01Signer} from "@welshman/signer" +import {makeEvent} from "@welshman/util" +import {ClientMessageType} from "../src/message" +import {MockAdapter} from "../src/adapter" +import {SingleRequest, MultiRequest, RequestEvent} from "../src/request" describe("SingleRequest", () => { beforeEach(() => { @@ -16,9 +16,9 @@ describe("SingleRequest", () => { it("everything basically works", async () => { const sendSpy = vi.fn() - const adapter = new MockAdapter('1', sendSpy) + const adapter = new MockAdapter("1", sendSpy) const req = new SingleRequest({ - relay: 'whatever', + relay: "whatever", filters: [{kinds: [1]}], context: {getAdapter: () => adapter}, }) @@ -82,14 +82,14 @@ describe("MultiRequest", () => { it("everything basically works", async () => { const send1Spy = vi.fn() - const adapter1 = new MockAdapter('1', send1Spy) + const adapter1 = new MockAdapter("1", send1Spy) const send2Spy = vi.fn() - const adapter2 = new MockAdapter('2', send2Spy) + const adapter2 = new MockAdapter("2", send2Spy) const req = new MultiRequest({ - relays: ['1', '2'], + relays: ["1", "2"], filters: [{kinds: [1]}], context: { - getAdapter: (url: string) => url === '1' ? adapter1 : adapter2 + getAdapter: (url: string) => (url === "1" ? adapter1 : adapter2), }, }) @@ -129,10 +129,10 @@ describe("MultiRequest", () => { await vi.runAllTimersAsync() - expect(duplicateSpy).toHaveBeenCalledWith(event1, '2') - expect(filteredSpy).toHaveBeenCalledWith(event2, '1') - expect(invalidSpy).toHaveBeenCalledWith(event3, '1') - expect(eventSpy).toHaveBeenCalledWith(event1, '1') + expect(duplicateSpy).toHaveBeenCalledWith(event1, "2") + expect(filteredSpy).toHaveBeenCalledWith(event2, "1") + expect(invalidSpy).toHaveBeenCalledWith(event3, "1") + expect(eventSpy).toHaveBeenCalledWith(event1, "1") expect(eoseSpy).toHaveBeenCalledTimes(0) adapter1.receive(["EOSE", id1]) @@ -145,4 +145,3 @@ describe("MultiRequest", () => { expect(closeSpy).toHaveBeenCalledTimes(1) }) }) - diff --git a/packages/net/__tests__/socket.test.ts b/packages/net/__tests__/socket.test.ts index 97ebaa3..f5e9c58 100644 --- a/packages/net/__tests__/socket.test.ts +++ b/packages/net/__tests__/socket.test.ts @@ -1,10 +1,9 @@ -import { sleep } from "@welshman/lib" -import WebSocket from 'isomorphic-ws' -import { afterEach, beforeEach, describe, expect, it, vi } from "vitest" -import { Socket, SocketStatus, SocketEvent } from "../src/socket" -import { ClientMessage, RelayMessage } from "../src/message" +import WebSocket from "isomorphic-ws" +import {afterEach, beforeEach, describe, expect, it, vi} from "vitest" +import {Socket, SocketStatus, SocketEvent} from "../src/socket" +import {ClientMessage, RelayMessage} from "../src/message" -vi.mock('isomorphic-ws', () => { +vi.mock("isomorphic-ws", () => { const WebSocket = vi.fn(function (this: any) { setTimeout(() => this.onopen()) }) @@ -15,7 +14,7 @@ vi.mock('isomorphic-ws', () => { this.onclose() }) - return { default: WebSocket } + return {default: WebSocket} }) describe("Socket", () => { @@ -77,9 +76,11 @@ describe("Socket", () => { socket.open() + const closeSpy = vi.spyOn(socket._ws, "close") + socket.close() - expect(socket._ws!.close).toHaveBeenCalled() + expect(closeSpy).toHaveBeenCalled() expect(statusSpy).toHaveBeenCalledWith(SocketStatus.Closed, "wss://test.relay") }) }) @@ -89,7 +90,7 @@ describe("Socket", () => { const enqueueSpy = vi.fn() socket.on(SocketEvent.Sending, enqueueSpy) - const message: ClientMessage = ["EVENT", { id: "123", kind: 1 }] + const message: ClientMessage = ["EVENT", {id: "123", kind: 1}] socket.send(message) expect(enqueueSpy).toHaveBeenCalledWith(message, "wss://test.relay") @@ -102,7 +103,7 @@ describe("Socket", () => { socket.open() 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) await vi.runAllTimers() @@ -118,7 +119,7 @@ describe("Socket", () => { socket.on(SocketEvent.Receive, receiveSpy) 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)} as unknown as any) await vi.runAllTimers() diff --git a/packages/net/src/negentropy.ts b/packages/net/src/negentropy.ts index 73a91b4..a1693b1 100644 --- a/packages/net/src/negentropy.ts +++ b/packages/net/src/negentropy.ts @@ -1,4 +1,5 @@ // (C) 2023 Doug Hoyte. MIT license +/* eslint-disable */ // @ts-nocheck const PROTOCOL_VERSION = 0x61 // Version 1 @@ -6,585 +7,611 @@ const ID_SIZE = 32 const FINGERPRINT_SIZE = 16 const Mode = { - Skip: 0, - Fingerprint: 1, - IdList: 2, + Skip: 0, + Fingerprint: 1, + IdList: 2, } class WrappedBuffer { - constructor(buffer) { - this._raw = new Uint8Array(buffer || 512) - this.length = buffer ? buffer.length : 0 + constructor(buffer) { + this._raw = new Uint8Array(buffer || 512) + this.length = buffer ? buffer.length : 0 + } + + unwrap() { + return this._raw.subarray(0, this.length) + } + + get capacity() { + return this._raw.byteLength + } + + extend(buf) { + if (buf._raw) buf = buf.unwrap() + if (typeof buf.length !== "number") throw Error("bad length") + const targetSize = buf.length + this.length + if (this.capacity < targetSize) { + const oldRaw = this._raw + const newCapacity = Math.max(this.capacity * 2, targetSize) + this._raw = new Uint8Array(newCapacity) + this._raw.set(oldRaw) } - unwrap() { - return this._raw.subarray(0, this.length) - } + this._raw.set(buf, this.length) + this.length += buf.length + } - get capacity() { - return this._raw.byteLength - } + shift() { + const first = this._raw[0] + this._raw = this._raw.subarray(1) + this.length-- + return first + } - extend(buf) { - if (buf._raw) buf = buf.unwrap() - if (typeof(buf.length) !== 'number') throw Error("bad length") - const targetSize = buf.length + this.length - if (this.capacity < targetSize) { - const oldRaw = this._raw - const newCapacity = Math.max(this.capacity * 2, targetSize) - this._raw = new Uint8Array(newCapacity) - this._raw.set(oldRaw) - } - - this._raw.set(buf, this.length) - this.length += buf.length - } - - shift() { - const first = this._raw[0] - this._raw = this._raw.subarray(1) - this.length-- - return first - } - - shiftN(n = 1) { - const firstSubarray = this._raw.subarray(0, n) - this._raw = this._raw.subarray(n) - this.length -= n - return firstSubarray - } + shiftN(n = 1) { + const firstSubarray = this._raw.subarray(0, n) + this._raw = this._raw.subarray(n) + this.length -= n + return firstSubarray + } } function decodeVarInt(buf) { - let res = 0 + let res = 0 - while (1) { - if (buf.length === 0) throw Error("parse ends prematurely") - const byte = buf.shift() - res = (res << 7) | (byte & 127) - if ((byte & 128) === 0) break - } + while (1) { + if (buf.length === 0) throw Error("parse ends prematurely") + const byte = buf.shift() + res = (res << 7) | (byte & 127) + if ((byte & 128) === 0) break + } - return res + return res } function encodeVarInt(n) { - if (n === 0) return new WrappedBuffer([0]) + if (n === 0) return new WrappedBuffer([0]) - const o = [] + const o = [] - while (n !== 0) { - o.push(n & 127) - n >>>= 7 - } + while (n !== 0) { + o.push(n & 127) + n >>>= 7 + } - o.reverse() + o.reverse() - for (let i = 0; i < o.length - 1; i++) o[i] |= 128 + for (let i = 0; i < o.length - 1; i++) o[i] |= 128 - return new WrappedBuffer(o) + return new WrappedBuffer(o) } function getByte(buf) { - return getBytes(buf, 1)[0] + return getBytes(buf, 1)[0] } function getBytes(buf, n) { - if (buf.length < n) throw Error("parse ends prematurely") - return buf.shiftN(n) + if (buf.length < n) throw Error("parse ends prematurely") + return buf.shiftN(n) } - class Accumulator { - constructor() { - this.setToZero() + constructor() { + this.setToZero() - if (typeof window === 'undefined') { // node.js - const crypto = require('crypto') - this.sha256 = async (slice) => new Uint8Array(crypto.createHash('sha256').update(slice).digest()) - } else { // browser - this.sha256 = async (slice) => new Uint8Array(await crypto.subtle.digest("SHA-256", slice)) - } + if (typeof window === "undefined") { + // node.js + const crypto = require("crypto") + this.sha256 = async slice => + new Uint8Array(crypto.createHash("sha256").update(slice).digest()) + } else { + // browser + this.sha256 = async slice => new Uint8Array(await crypto.subtle.digest("SHA-256", slice)) + } + } + + setToZero() { + this.buf = new Uint8Array(ID_SIZE) + } + + add(otherBuf) { + let currCarry = 0, + nextCarry = 0 + const p = new DataView(this.buf.buffer) + const po = new DataView(otherBuf.buffer) + + for (let i = 0; i < 8; i++) { + const offset = i * 4 + const orig = p.getUint32(offset, true) + const otherV = po.getUint32(offset, true) + + let next = orig + + next += currCarry + next += otherV + if (next > 0xffffffff) nextCarry = 1 + + p.setUint32(offset, next & 0xffffffff, true) + currCarry = nextCarry + nextCarry = 0 + } + } + + negate() { + const p = new DataView(this.buf.buffer) + + for (let i = 0; i < 8; i++) { + const offset = i * 4 + p.setUint32(offset, ~p.getUint32(offset, true)) } - setToZero() { - this.buf = new Uint8Array(ID_SIZE) - } + const one = new Uint8Array(ID_SIZE) + one[0] = 1 + this.add(one) + } - add(otherBuf) { - let currCarry = 0, nextCarry = 0 - const p = new DataView(this.buf.buffer) - const po = new DataView(otherBuf.buffer) + async getFingerprint(n) { + const input = new WrappedBuffer() + input.extend(this.buf) + input.extend(encodeVarInt(n)) - for (let i = 0; i < 8; i++) { - const offset = i * 4 - const orig = p.getUint32(offset, true) - const otherV = po.getUint32(offset, true) + const hash = await this.sha256(input.unwrap()) - let next = orig - - next += currCarry - next += otherV - if (next > 0xFFFFFFFF) nextCarry = 1 - - p.setUint32(offset, next & 0xFFFFFFFF, true) - currCarry = nextCarry - nextCarry = 0 - } - } - - negate() { - const p = new DataView(this.buf.buffer) - - for (let i = 0; i < 8; i++) { - const offset = i * 4 - p.setUint32(offset, ~p.getUint32(offset, true)) - } - - const one = new Uint8Array(ID_SIZE) - one[0] = 1 - this.add(one) - } - - async getFingerprint(n) { - const input = new WrappedBuffer() - input.extend(this.buf) - input.extend(encodeVarInt(n)) - - const hash = await this.sha256(input.unwrap()) - - return hash.subarray(0, FINGERPRINT_SIZE) - } + return hash.subarray(0, FINGERPRINT_SIZE) + } } - class NegentropyStorageVector { - constructor() { - this.items = [] - this.sealed = false + constructor() { + this.items = [] + this.sealed = false + } + + insert(timestamp, id) { + if (this.sealed) throw Error("already sealed") + id = loadInputBuffer(id) + if (id.byteLength !== ID_SIZE) throw Error("bad id size for added item") + this.items.push({timestamp, id}) + } + + seal() { + if (this.sealed) throw Error("already sealed") + this.sealed = true + + this.items.sort(itemCompare) + + for (let i = 1; i < this.items.length; i++) { + if (itemCompare(this.items[i - 1], this.items[i]) === 0) + throw Error("duplicate item inserted") + } + } + + unseal() { + this.sealed = false + } + + size() { + this._checkSealed() + return this.items.length + } + + getItem(i) { + this._checkSealed() + if (i >= this.items.length) throw Error("out of range") + return this.items[i] + } + + iterate(begin, end, cb) { + this._checkSealed() + this._checkBounds(begin, end) + + for (let i = begin; i < end; ++i) { + if (!cb(this.items[i], i)) break + } + } + + findLowerBound(begin, end, bound) { + this._checkSealed() + this._checkBounds(begin, end) + + return this._binarySearch(this.items, begin, end, a => itemCompare(a, bound) < 0) + } + + async fingerprint(begin, end) { + const out = new Accumulator() + out.setToZero() + + this.iterate(begin, end, (item, i) => { + out.add(item.id) + return true + }) + + return await out.getFingerprint(end - begin) + } + + _checkSealed() { + if (!this.sealed) throw Error("not sealed") + } + + _checkBounds(begin, end) { + if (begin > end || end > this.items.length) throw Error("bad range") + } + + _binarySearch(arr, first, last, cmp) { + let count = last - first + + while (count > 0) { + let it = first + const step = Math.floor(count / 2) + it += step + + if (cmp(arr[it])) { + first = ++it + count -= step + 1 + } else { + count = step + } } - insert(timestamp, id) { - if (this.sealed) throw Error("already sealed") - id = loadInputBuffer(id) - if (id.byteLength !== ID_SIZE) throw Error("bad id size for added item") - this.items.push({timestamp, id}) - } - - seal() { - if (this.sealed) throw Error("already sealed") - this.sealed = true - - this.items.sort(itemCompare) - - for (let i = 1; i < this.items.length; i++) { - if (itemCompare(this.items[i - 1], this.items[i]) === 0) throw Error("duplicate item inserted") - } - } - - unseal() { - this.sealed = false - } - - size() { - this._checkSealed() - return this.items.length - } - - getItem(i) { - this._checkSealed() - if (i >= this.items.length) throw Error("out of range") - return this.items[i] - } - - iterate(begin, end, cb) { - this._checkSealed() - this._checkBounds(begin, end) - - for (let i = begin; i < end; ++i) { - if (!cb(this.items[i], i)) break - } - } - - findLowerBound(begin, end, bound) { - this._checkSealed() - this._checkBounds(begin, end) - - return this._binarySearch(this.items, begin, end, (a) => itemCompare(a, bound) < 0) - } - - async fingerprint(begin, end) { - const out = new Accumulator() - out.setToZero() - - this.iterate(begin, end, (item, i) => { - out.add(item.id) - return true - }) - - return await out.getFingerprint(end - begin) - } - - _checkSealed() { - if (!this.sealed) throw Error("not sealed") - } - - _checkBounds(begin, end) { - if (begin > end || end > this.items.length) throw Error("bad range") - } - - _binarySearch(arr, first, last, cmp) { - let count = last - first - - while (count > 0) { - let it = first - const step = Math.floor(count / 2) - it += step - - if (cmp(arr[it])) { - first = ++it - count -= step + 1 - } else { - count = step - } - } - - return first - } + return first + } } - class Negentropy { - constructor(storage, frameSizeLimit = 0) { - if (frameSizeLimit !== 0 && frameSizeLimit < 4096) throw Error("frameSizeLimit too small") + constructor(storage, frameSizeLimit = 0) { + if (frameSizeLimit !== 0 && frameSizeLimit < 4096) throw Error("frameSizeLimit too small") - this.storage = storage - this.frameSizeLimit = frameSizeLimit + this.storage = storage + this.frameSizeLimit = frameSizeLimit - this.lastTimestampIn = 0 - this.lastTimestampOut = 0 + this.lastTimestampIn = 0 + this.lastTimestampOut = 0 + } + + _bound(timestamp, id) { + return {timestamp, id: id ? id : new Uint8Array(0)} + } + + async initiate() { + if (this.isInitiator) throw Error("already initiated") + this.isInitiator = true + + const output = new WrappedBuffer() + output.extend([PROTOCOL_VERSION]) + + await this.splitRange(0, this.storage.size(), this._bound(Number.MAX_VALUE), output) + + return this._renderOutput(output) + } + + setInitiator() { + this.isInitiator = true + } + + async reconcile(query) { + const haveIds = [], + needIds = [] + query = new WrappedBuffer(loadInputBuffer(query)) + + this.lastTimestampIn = this.lastTimestampOut = 0 // reset for each message + + const fullOutput = new WrappedBuffer() + fullOutput.extend([PROTOCOL_VERSION]) + + const protocolVersion = getByte(query) + if (protocolVersion < 0x60 || protocolVersion > 0x6f) + throw Error("invalid negentropy protocol version byte") + if (protocolVersion !== PROTOCOL_VERSION) { + if (this.isInitiator) + throw Error( + "unsupported negentropy protocol version requested: " + (protocolVersion - 0x60), + ) + else return [this._renderOutput(fullOutput), haveIds, needIds] } - _bound(timestamp, id) { - return {timestamp, id: id ? id : new Uint8Array(0)} - } + const storageSize = this.storage.size() + let prevBound = this._bound(0) + let prevIndex = 0 + let skip = false - async initiate() { - if (this.isInitiator) throw Error("already initiated") - this.isInitiator = true + while (query.length !== 0) { + let o = new WrappedBuffer() - const output = new WrappedBuffer() - output.extend([PROTOCOL_VERSION]) - - await this.splitRange(0, this.storage.size(), this._bound(Number.MAX_VALUE), output) - - return this._renderOutput(output) - } - - setInitiator() { - this.isInitiator = true - } - - async reconcile(query) { - const haveIds = [], needIds = [] - query = new WrappedBuffer(loadInputBuffer(query)) - - this.lastTimestampIn = this.lastTimestampOut = 0 // reset for each message - - const fullOutput = new WrappedBuffer() - fullOutput.extend([PROTOCOL_VERSION]) - - const protocolVersion = getByte(query) - if (protocolVersion < 0x60 || protocolVersion > 0x6F) throw Error("invalid negentropy protocol version byte") - if (protocolVersion !== PROTOCOL_VERSION) { - if (this.isInitiator) throw Error("unsupported negentropy protocol version requested: " + (protocolVersion - 0x60)) - else return [this._renderOutput(fullOutput), haveIds, needIds] + const doSkip = () => { + if (skip) { + skip = false + o.extend(this.encodeBound(prevBound)) + o.extend(encodeVarInt(Mode.Skip)) } + } - const storageSize = this.storage.size() - let prevBound = this._bound(0) - let prevIndex = 0 - let skip = false + const currBound = this.decodeBound(query) + const mode = decodeVarInt(query) - while (query.length !== 0) { - let o = new WrappedBuffer() + const lower = prevIndex + let upper = this.storage.findLowerBound(prevIndex, storageSize, currBound) - const doSkip = () => { - if (skip) { - skip = false - o.extend(this.encodeBound(prevBound)) - o.extend(encodeVarInt(Mode.Skip)) - } - } + if (mode === Mode.Skip) { + skip = true + } else if (mode === Mode.Fingerprint) { + const theirFingerprint = getBytes(query, FINGERPRINT_SIZE) + const ourFingerprint = await this.storage.fingerprint(lower, upper) - const currBound = this.decodeBound(query) - const mode = decodeVarInt(query) - - const lower = prevIndex - let upper = this.storage.findLowerBound(prevIndex, storageSize, currBound) - - if (mode === Mode.Skip) { - skip = true - } else if (mode === Mode.Fingerprint) { - const theirFingerprint = getBytes(query, FINGERPRINT_SIZE) - const ourFingerprint = await this.storage.fingerprint(lower, upper) - - if (compareUint8Array(theirFingerprint, ourFingerprint) !== 0) { - doSkip() - await this.splitRange(lower, upper, currBound, o) - } else { - skip = true - } - } else if (mode === Mode.IdList) { - const numIds = decodeVarInt(query) - - const theirElems = {} // stringified Uint8Array -> original Uint8Array (or hex) - for (let i = 0; i < numIds; i++) { - const e = getBytes(query, ID_SIZE) - if (this.isInitiator) theirElems[e] = e - } - - if (this.isInitiator) { - skip = true - - this.storage.iterate(lower, upper, (item) => { - const k = item.id - - if (!theirElems[k]) { - // ID exists on our side, but not their side - if (this.isInitiator) haveIds.push(this.wantUint8ArrayOutput ? k : uint8ArrayToHex(k)) - } else { - // ID exists on both sides - delete theirElems[k] - } - - return true - }) - - for (const v of Object.values(theirElems)) { - // ID exists on their side, but not our side - needIds.push(this.wantUint8ArrayOutput ? v : uint8ArrayToHex(v)) - } - } else { - doSkip() - - const responseIds = new WrappedBuffer() - let numResponseIds = 0 - let endBound = currBound - - this.storage.iterate(lower, upper, (item, index) => { - if (this.exceededFrameSizeLimit(fullOutput.length + responseIds.length)) { - endBound = item - upper = index // shrink upper so that remaining range gets correct fingerprint - return false - } - - responseIds.extend(item.id) - numResponseIds++ - return true - }) - - o.extend(this.encodeBound(endBound)) - o.extend(encodeVarInt(Mode.IdList)) - o.extend(encodeVarInt(numResponseIds)) - o.extend(responseIds) - - fullOutput.extend(o) - o = new WrappedBuffer() - } - } else { - throw Error("unexpected mode") - } - - if (this.exceededFrameSizeLimit(fullOutput.length + o.length)) { - // frameSizeLimit exceeded: Stop range processing and return a fingerprint for the remaining range - const remainingFingerprint = await this.storage.fingerprint(upper, storageSize) - - fullOutput.extend(this.encodeBound(this._bound(Number.MAX_VALUE))) - fullOutput.extend(encodeVarInt(Mode.Fingerprint)) - fullOutput.extend(remainingFingerprint) - break - } else { - fullOutput.extend(o) - } - - prevIndex = upper - prevBound = currBound - } - - return [fullOutput.length === 1 && this.isInitiator ? null : this._renderOutput(fullOutput), haveIds, needIds] - } - - async splitRange(lower, upper, upperBound, o) { - const numElems = upper - lower - const buckets = 16 - - if (numElems < buckets * 2) { - o.extend(this.encodeBound(upperBound)) - o.extend(encodeVarInt(Mode.IdList)) - - o.extend(encodeVarInt(numElems)) - this.storage.iterate(lower, upper, (item) => { - o.extend(item.id) - return true - }) + if (compareUint8Array(theirFingerprint, ourFingerprint) !== 0) { + doSkip() + await this.splitRange(lower, upper, currBound, o) } else { - const itemsPerBucket = Math.floor(numElems / buckets) - const bucketsWithExtra = numElems % buckets - let curr = lower + skip = true + } + } else if (mode === Mode.IdList) { + const numIds = decodeVarInt(query) - for (let i = 0; i < buckets; i++) { - const bucketSize = itemsPerBucket + (i < bucketsWithExtra ? 1 : 0) - const ourFingerprint = await this.storage.fingerprint(curr, curr + bucketSize) - curr += bucketSize + const theirElems = {} // stringified Uint8Array -> original Uint8Array (or hex) + for (let i = 0; i < numIds; i++) { + const e = getBytes(query, ID_SIZE) + if (this.isInitiator) theirElems[e] = e + } - let nextBound + if (this.isInitiator) { + skip = true - if (curr === upper) { - nextBound = upperBound - } else { - let prevItem, currItem + this.storage.iterate(lower, upper, item => { + const k = item.id - this.storage.iterate(curr - 1, curr + 1, (item, index) => { - if (index === curr - 1) prevItem = item - else currItem = item - return true - }) - - nextBound = this.getMinimalBound(prevItem, currItem) - } - - o.extend(this.encodeBound(nextBound)) - o.extend(encodeVarInt(Mode.Fingerprint)) - o.extend(ourFingerprint) + if (!theirElems[k]) { + // ID exists on our side, but not their side + if (this.isInitiator) haveIds.push(this.wantUint8ArrayOutput ? k : uint8ArrayToHex(k)) + } else { + // ID exists on both sides + delete theirElems[k] } - } - } - _renderOutput(o) { - o = o.unwrap() - if (!this.wantUint8ArrayOutput) o = uint8ArrayToHex(o) - return o - } + return true + }) - exceededFrameSizeLimit(n) { - return this.frameSizeLimit && n > this.frameSizeLimit - 200 - } - - // Decoding - - decodeTimestampIn(encoded) { - let timestamp = decodeVarInt(encoded) - timestamp = timestamp === 0 ? Number.MAX_VALUE : timestamp - 1 - if (this.lastTimestampIn === Number.MAX_VALUE || timestamp === Number.MAX_VALUE) { - this.lastTimestampIn = Number.MAX_VALUE - return Number.MAX_VALUE - } - timestamp += this.lastTimestampIn - this.lastTimestampIn = timestamp - return timestamp - } - - decodeBound(encoded) { - const timestamp = this.decodeTimestampIn(encoded) - const len = decodeVarInt(encoded) - if (len > ID_SIZE) throw Error("bound key too long") - const id = getBytes(encoded, len) - return {timestamp, id} - } - - // Encoding - - encodeTimestampOut(timestamp) { - if (timestamp === Number.MAX_VALUE) { - this.lastTimestampOut = Number.MAX_VALUE - return encodeVarInt(0) - } - - const temp = timestamp - timestamp -= this.lastTimestampOut - this.lastTimestampOut = temp - return encodeVarInt(timestamp + 1) - } - - encodeBound(key) { - const output = new WrappedBuffer() - - output.extend(this.encodeTimestampOut(key.timestamp)) - output.extend(encodeVarInt(key.id.length)) - output.extend(key.id) - - return output - } - - getMinimalBound(prev, curr) { - if (curr.timestamp !== prev.timestamp) { - return this._bound(curr.timestamp) + for (const v of Object.values(theirElems)) { + // ID exists on their side, but not our side + needIds.push(this.wantUint8ArrayOutput ? v : uint8ArrayToHex(v)) + } } else { - let sharedPrefixBytes = 0 - const currKey = curr.id - const prevKey = prev.id + doSkip() - for (let i = 0; i < ID_SIZE; i++) { - if (currKey[i] !== prevKey[i]) break - sharedPrefixBytes++ + const responseIds = new WrappedBuffer() + let numResponseIds = 0 + let endBound = currBound + + this.storage.iterate(lower, upper, (item, index) => { + if (this.exceededFrameSizeLimit(fullOutput.length + responseIds.length)) { + endBound = item + upper = index // shrink upper so that remaining range gets correct fingerprint + return false } - return this._bound(curr.timestamp, curr.id.subarray(0, sharedPrefixBytes + 1)) + responseIds.extend(item.id) + numResponseIds++ + return true + }) + + o.extend(this.encodeBound(endBound)) + o.extend(encodeVarInt(Mode.IdList)) + o.extend(encodeVarInt(numResponseIds)) + o.extend(responseIds) + + fullOutput.extend(o) + o = new WrappedBuffer() } + } else { + throw Error("unexpected mode") + } + + if (this.exceededFrameSizeLimit(fullOutput.length + o.length)) { + // frameSizeLimit exceeded: Stop range processing and return a fingerprint for the remaining range + const remainingFingerprint = await this.storage.fingerprint(upper, storageSize) + + fullOutput.extend(this.encodeBound(this._bound(Number.MAX_VALUE))) + fullOutput.extend(encodeVarInt(Mode.Fingerprint)) + fullOutput.extend(remainingFingerprint) + break + } else { + fullOutput.extend(o) + } + + prevIndex = upper + prevBound = currBound } + + return [ + fullOutput.length === 1 && this.isInitiator ? null : this._renderOutput(fullOutput), + haveIds, + needIds, + ] + } + + async splitRange(lower, upper, upperBound, o) { + const numElems = upper - lower + const buckets = 16 + + if (numElems < buckets * 2) { + o.extend(this.encodeBound(upperBound)) + o.extend(encodeVarInt(Mode.IdList)) + + o.extend(encodeVarInt(numElems)) + this.storage.iterate(lower, upper, item => { + o.extend(item.id) + return true + }) + } else { + const itemsPerBucket = Math.floor(numElems / buckets) + const bucketsWithExtra = numElems % buckets + let curr = lower + + for (let i = 0; i < buckets; i++) { + const bucketSize = itemsPerBucket + (i < bucketsWithExtra ? 1 : 0) + const ourFingerprint = await this.storage.fingerprint(curr, curr + bucketSize) + curr += bucketSize + + let nextBound + + if (curr === upper) { + nextBound = upperBound + } else { + let prevItem, currItem + + this.storage.iterate(curr - 1, curr + 1, (item, index) => { + if (index === curr - 1) prevItem = item + else currItem = item + return true + }) + + nextBound = this.getMinimalBound(prevItem, currItem) + } + + o.extend(this.encodeBound(nextBound)) + o.extend(encodeVarInt(Mode.Fingerprint)) + o.extend(ourFingerprint) + } + } + } + + _renderOutput(o) { + o = o.unwrap() + if (!this.wantUint8ArrayOutput) o = uint8ArrayToHex(o) + return o + } + + exceededFrameSizeLimit(n) { + return this.frameSizeLimit && n > this.frameSizeLimit - 200 + } + + // Decoding + + decodeTimestampIn(encoded) { + let timestamp = decodeVarInt(encoded) + timestamp = timestamp === 0 ? Number.MAX_VALUE : timestamp - 1 + if (this.lastTimestampIn === Number.MAX_VALUE || timestamp === Number.MAX_VALUE) { + this.lastTimestampIn = Number.MAX_VALUE + return Number.MAX_VALUE + } + timestamp += this.lastTimestampIn + this.lastTimestampIn = timestamp + return timestamp + } + + decodeBound(encoded) { + const timestamp = this.decodeTimestampIn(encoded) + const len = decodeVarInt(encoded) + if (len > ID_SIZE) throw Error("bound key too long") + const id = getBytes(encoded, len) + return {timestamp, id} + } + + // Encoding + + encodeTimestampOut(timestamp) { + if (timestamp === Number.MAX_VALUE) { + this.lastTimestampOut = Number.MAX_VALUE + return encodeVarInt(0) + } + + const temp = timestamp + timestamp -= this.lastTimestampOut + this.lastTimestampOut = temp + return encodeVarInt(timestamp + 1) + } + + encodeBound(key) { + const output = new WrappedBuffer() + + output.extend(this.encodeTimestampOut(key.timestamp)) + output.extend(encodeVarInt(key.id.length)) + output.extend(key.id) + + return output + } + + getMinimalBound(prev, curr) { + if (curr.timestamp !== prev.timestamp) { + return this._bound(curr.timestamp) + } else { + let sharedPrefixBytes = 0 + const currKey = curr.id + const prevKey = prev.id + + for (let i = 0; i < ID_SIZE; i++) { + if (currKey[i] !== prevKey[i]) break + sharedPrefixBytes++ + } + + return this._bound(curr.timestamp, curr.id.subarray(0, sharedPrefixBytes + 1)) + } + } } function loadInputBuffer(inp) { - if (typeof(inp) === 'string') inp = hexToUint8Array(inp) - else if (__proto__ !== Uint8Array.prototype) inp = new Uint8Array(inp) // node Buffer? - return inp + if (typeof inp === "string") inp = hexToUint8Array(inp) + else if (__proto__ !== Uint8Array.prototype) inp = new Uint8Array(inp) // node Buffer? + return inp } function hexToUint8Array(h) { - if (h.startsWith('0x')) h = h.substr(2) - if (h.length % 2 === 1) throw Error("odd length of hex string") - const arr = new Uint8Array(h.length / 2) - for (let i = 0; i < arr.length; i++) arr[i] = parseInt(h.substr(i * 2, 2), 16) - return arr + if (h.startsWith("0x")) h = h.substr(2) + if (h.length % 2 === 1) throw Error("odd length of hex string") + const arr = new Uint8Array(h.length / 2) + for (let i = 0; i < arr.length; i++) arr[i] = parseInt(h.substr(i * 2, 2), 16) + return arr } const uint8ArrayToHexLookupTable = new Array(256) { - const hexAlphabet = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'] - for (let i = 0; i < 256; i++) { - uint8ArrayToHexLookupTable[i] = hexAlphabet[(i >>> 4) & 0xF] + hexAlphabet[i & 0xF] - } + const hexAlphabet = [ + "0", + "1", + "2", + "3", + "4", + "5", + "6", + "7", + "8", + "9", + "a", + "b", + "c", + "d", + "e", + "f", + ] + for (let i = 0; i < 256; i++) { + uint8ArrayToHexLookupTable[i] = hexAlphabet[(i >>> 4) & 0xf] + hexAlphabet[i & 0xf] + } } function uint8ArrayToHex(arr) { - let out = '' - for (let i = 0, edx = arr.length; i < edx; i++) { - out += uint8ArrayToHexLookupTable[arr[i]] - } - return out + let out = "" + for (let i = 0, edx = arr.length; i < edx; i++) { + out += uint8ArrayToHexLookupTable[arr[i]] + } + return out } - function compareUint8Array(a, b) { - for (let i = 0; i < a.byteLength; i++) { - if (a[i] < b[i]) return -1 - if (a[i] > b[i]) return 1 - } + for (let i = 0; i < a.byteLength; i++) { + if (a[i] < b[i]) return -1 + if (a[i] > b[i]) return 1 + } - if (a.byteLength > b.byteLength) return 1 - if (a.byteLength < b.byteLength) return -1 + if (a.byteLength > b.byteLength) return 1 + if (a.byteLength < b.byteLength) return -1 - return 0 + return 0 } function itemCompare(a, b) { - if (a.timestamp === b.timestamp) { - return compareUint8Array(a.id, b.id) - } + if (a.timestamp === b.timestamp) { + return compareUint8Array(a.id, b.id) + } - return a.timestamp - b.timestamp + return a.timestamp - b.timestamp } - -export {Negentropy, NegentropyStorageVector,} +export {Negentropy, NegentropyStorageVector} diff --git a/packages/net/src/policy.ts b/packages/net/src/policy.ts index 48ff41b..ddbb063 100644 --- a/packages/net/src/policy.ts +++ b/packages/net/src/policy.ts @@ -1,4 +1,4 @@ -import {on, nthEq, always, call, sleep, spec, ago, now} from "@welshman/lib" +import {on, nthEq, always, call, sleep, ago, now} from "@welshman/lib" import {AUTH_JOIN, StampedEvent, SignedEvent} from "@welshman/util" import { ClientMessage, @@ -7,7 +7,6 @@ import { isClientEvent, isClientReq, isClientNegClose, - ClientMessageType, RelayMessage, isRelayOk, isRelayEose, @@ -50,7 +49,7 @@ export const socketPolicyAuthBuffer = (socket: Socket) => { }), on(socket, SocketEvent.Receiving, (message: RelayMessage) => { // If the client is closing a request during auth, don't tell the caller, we'll retry it - if (isRelayClosed(message) && message[2]?.startsWith('auth-required:')) { + if (isRelayClosed(message) && message[2]?.startsWith("auth-required:")) { socket._recvQueue.remove(message) } @@ -60,7 +59,7 @@ export const socketPolicyAuthBuffer = (socket: Socket) => { } // If the client is rejecting an event during auth, don't tell the caller, we'll retry it - if (isRelayOk(message) && !message[2] && message[3]?.startsWith('auth-required:')) { + if (isRelayOk(message) && !message[2] && message[3]?.startsWith("auth-required:")) { socket._recvQueue.remove(message) } }), diff --git a/packages/net/src/request.ts b/packages/net/src/request.ts index eb749e0..c41d2dd 100644 --- a/packages/net/src/request.ts +++ b/packages/net/src/request.ts @@ -289,7 +289,7 @@ export const makeLoader = (options: LoaderOptions) => tracker, relays: [relay], autoClose: true, - ...options + ...options, }) let count = 0 diff --git a/packages/relay/__tests__/relay.test.ts b/packages/relay/__tests__/relay.test.ts index 932f674..ed5173f 100644 --- a/packages/relay/__tests__/relay.test.ts +++ b/packages/relay/__tests__/relay.test.ts @@ -19,7 +19,6 @@ describe("LocalRelay", () => { const id = "ff".repeat(32) const sig = "00".repeat(64) const currentTime = now() - const onionUrl = "abcdefghijklmnopqrstuvwxyz234567abcdefghijklmnopqrstuvwx.onion" const createEvent = (overrides = {}): TrustedEvent => ({ id: id, diff --git a/packages/store/__tests__/index.test.ts b/packages/store/__tests__/index.test.ts index a63c820..b078d66 100644 --- a/packages/store/__tests__/index.test.ts +++ b/packages/store/__tests__/index.test.ts @@ -3,7 +3,6 @@ import {Repository} from "@welshman/relay" import {get} from "svelte/store" import {afterEach, beforeEach, describe, expect, it, vi} from "vitest" import { - adapter, custom, deriveEvents, deriveEventsMapped, @@ -114,28 +113,6 @@ describe("Store utilities", () => { }) }) - describe("adapter", () => { - it("should adapt between different types", () => { - const source = synced("test", 0) - const adapted = adapter({ - store: source, - forward: n => n.toString(), - backward: s => parseInt(s, 10), - }) - - const mockFn = vi.fn() - adapted.subscribe(mockFn) - - adapted.set("42") - expect(get(source)).toBe(42) - expect(mockFn).toHaveBeenLastCalledWith("42") - - adapted.update(s => (parseInt(s, 10) + 1).toString()) - expect(get(source)).toBe(43) - expect(mockFn).toHaveBeenLastCalledWith("43") - }) - }) - describe("Event-related stores", () => { const mockRepository = { query: vi.fn(), diff --git a/packages/store/src/index.ts b/packages/store/src/index.ts index 11de39a..c70b7b9 100644 --- a/packages/store/src/index.ts +++ b/packages/store/src/index.ts @@ -121,22 +121,6 @@ export const custom = ( } } -// Simple adapter - -export const adapter = ({ - store, - forward, - backward, -}: { - store: Writable - forward: (x: Source) => Target - backward: (x: Target) => Source -}) => ({ - ...derived(store, forward), - set: (x: Target) => store.set(backward(x)), - update: (f: (x: Target) => Target) => store.update((x: Source) => backward(f(forward(x)))), -}) - // Event related stores export type DeriveEventsMappedOptions = { diff --git a/packages/util/__tests__/Events.test.ts b/packages/util/__tests__/Events.test.ts index 8bf77e3..4ae7f15 100644 --- a/packages/util/__tests__/Events.test.ts +++ b/packages/util/__tests__/Events.test.ts @@ -184,7 +184,7 @@ describe("Events", () => { describe("signature validation", () => { it("should validate signature using verifiedSymbol", () => { - let event = createSignedEvent() as Events.SignedEvent + const event = createSignedEvent() as Events.SignedEvent event[verifiedSymbol] = true expect(Events.verifyEvent(event)).toBe(true) diff --git a/packages/util/__tests__/Filters.test.ts b/packages/util/__tests__/Filters.test.ts index 08b655c..197839c 100644 --- a/packages/util/__tests__/Filters.test.ts +++ b/packages/util/__tests__/Filters.test.ts @@ -211,9 +211,7 @@ describe("Filters", () => { it("should calculate filter generality", () => { 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(getFilterGenerality({authors: [pubkey, pubkey, pubkey], kinds: [1]})).toBe(0.01) expect(getFilterGenerality({kinds: [1]})).toBe(1) }) diff --git a/packages/util/src/Events.ts b/packages/util/src/Events.ts index 16c28e5..7fe80e8 100644 --- a/packages/util/src/Events.ts +++ b/packages/util/src/Events.ts @@ -72,7 +72,8 @@ export const verifyEvent = (() => { }) } - return (event: TrustedEvent) => event.sig && (event[verifiedSymbol] || verify(event as SignedEvent)) + return (event: TrustedEvent) => + event.sig && (event[verifiedSymbol] || verify(event as SignedEvent)) })() export const isEventTemplate = (e: EventTemplate): e is EventTemplate => diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 090db86..4a8b667 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -20,9 +20,6 @@ importers: eslint-plugin-prettier: specifier: ~5.2.5 version: 5.2.6(eslint-config-prettier@10.1.1(eslint@9.23.0))(eslint@9.23.0)(prettier@3.5.3) - eslint-plugin-react: - specifier: ~7.37.4 - version: 7.37.4(eslint@9.23.0) fake-indexeddb: specifier: ^6.0.0 version: 6.0.0 @@ -1404,46 +1401,10 @@ packages: resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} engines: {node: '>= 0.4'} - array-buffer-byte-length@1.0.2: - resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==} - engines: {node: '>= 0.4'} - - array-includes@3.1.8: - resolution: {integrity: sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==} - engines: {node: '>= 0.4'} - - array.prototype.findlast@1.2.5: - resolution: {integrity: sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==} - engines: {node: '>= 0.4'} - - array.prototype.flat@1.3.3: - resolution: {integrity: sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==} - engines: {node: '>= 0.4'} - - array.prototype.flatmap@1.3.3: - resolution: {integrity: sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==} - engines: {node: '>= 0.4'} - - array.prototype.tosorted@1.1.4: - resolution: {integrity: sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==} - engines: {node: '>= 0.4'} - - arraybuffer.prototype.slice@1.0.4: - resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==} - engines: {node: '>= 0.4'} - assertion-error@2.0.1: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} - async-function@1.0.0: - resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==} - engines: {node: '>= 0.4'} - - available-typed-arrays@1.0.7: - resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} - engines: {node: '>= 0.4'} - axobject-query@4.1.0: resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==} engines: {node: '>= 0.4'} @@ -1468,18 +1429,6 @@ packages: resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} engines: {node: '>=8'} - call-bind-apply-helpers@1.0.2: - resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} - engines: {node: '>= 0.4'} - - call-bind@1.0.8: - resolution: {integrity: sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==} - engines: {node: '>= 0.4'} - - call-bound@1.0.4: - resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} - engines: {node: '>= 0.4'} - callsites@3.1.0: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} @@ -1539,18 +1488,6 @@ packages: csstype@3.1.3: resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} - data-view-buffer@1.0.2: - resolution: {integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==} - engines: {node: '>= 0.4'} - - data-view-byte-length@1.0.2: - resolution: {integrity: sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==} - engines: {node: '>= 0.4'} - - data-view-byte-offset@1.0.1: - resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==} - engines: {node: '>= 0.4'} - debug@4.4.0: resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==} engines: {node: '>=6.0'} @@ -1567,14 +1504,6 @@ packages: deep-is@0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} - define-data-property@1.1.4: - resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} - engines: {node: '>= 0.4'} - - define-properties@1.2.1: - resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} - engines: {node: '>= 0.4'} - dequal@2.0.3: resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} engines: {node: '>=6'} @@ -1582,14 +1511,6 @@ packages: devlop@1.1.0: resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} - doctrine@2.1.0: - resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} - engines: {node: '>=0.10.0'} - - dunder-proto@1.0.1: - resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} - engines: {node: '>= 0.4'} - eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} @@ -1606,41 +1527,9 @@ packages: resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} engines: {node: '>=0.12'} - es-abstract@1.23.9: - resolution: {integrity: sha512-py07lI0wjxAC/DcfK1S6G7iANonniZwTISvdPzk9hzeH0IZIshbuuFxLIU96OyF89Yb9hiqWn8M/bY83KY5vzA==} - engines: {node: '>= 0.4'} - - es-define-property@1.0.1: - resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} - engines: {node: '>= 0.4'} - - es-errors@1.3.0: - resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} - engines: {node: '>= 0.4'} - - es-iterator-helpers@1.2.1: - resolution: {integrity: sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w==} - engines: {node: '>= 0.4'} - es-module-lexer@1.6.0: resolution: {integrity: sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==} - es-object-atoms@1.1.1: - resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} - engines: {node: '>= 0.4'} - - es-set-tostringtag@2.1.0: - resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} - engines: {node: '>= 0.4'} - - es-shim-unscopables@1.1.0: - resolution: {integrity: sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==} - engines: {node: '>= 0.4'} - - es-to-primitive@1.3.0: - resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==} - engines: {node: '>= 0.4'} - esbuild@0.21.5: resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} engines: {node: '>=12'} @@ -1675,12 +1564,6 @@ packages: eslint-config-prettier: optional: true - eslint-plugin-react@7.37.4: - resolution: {integrity: sha512-BGP0jRmfYyvOyvMoRX/uoUeW+GqNj9y16bPQzqAHf3AYII/tDs+jMN0dBVkl88/OZwNGwrVFxE7riHsXVfy/LQ==} - engines: {node: '>=4'} - peerDependencies: - eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7 - eslint-scope@8.3.0: resolution: {integrity: sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -1782,10 +1665,6 @@ packages: focus-trap@7.6.4: resolution: {integrity: sha512-xx560wGBk7seZ6y933idtjJQc1l+ck+pI3sKvhKozdBV1dRZoKhkW5xoCaFv9tQiX5RH1xfSxjuNu6g+lmN/gw==} - for-each@0.3.5: - resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} - engines: {node: '>= 0.4'} - foreground-child@3.3.1: resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} engines: {node: '>=14'} @@ -1795,32 +1674,10 @@ packages: engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] - function-bind@1.1.2: - resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} - - function.prototype.name@1.1.8: - resolution: {integrity: sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==} - engines: {node: '>= 0.4'} - - functions-have-names@1.2.3: - resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} - fuse.js@7.1.0: resolution: {integrity: sha512-trLf4SzuuUxfusZADLINj+dE8clK1frKdmqiJNb1Es75fmI5oY6X2mxLVUciLLjxqw/xr72Dhy+lER6dGd02FQ==} engines: {node: '>=10'} - get-intrinsic@1.3.0: - resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} - engines: {node: '>= 0.4'} - - get-proto@1.0.1: - resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} - engines: {node: '>= 0.4'} - - get-symbol-description@1.1.0: - resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==} - engines: {node: '>= 0.4'} - glob-parent@5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} engines: {node: '>= 6'} @@ -1842,14 +1699,6 @@ packages: resolution: {integrity: sha512-iInW14XItCXET01CQFqudPOWP2jYMl7T+QRQT+UNcR/iQncN/F0UNpgd76iFkBPgNQb4+X3LV9tLJYzwh+Gl3A==} engines: {node: '>=18'} - globalthis@1.0.4: - resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} - engines: {node: '>= 0.4'} - - gopd@1.2.0: - resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} - engines: {node: '>= 0.4'} - graphemer@1.4.0: resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} @@ -1857,33 +1706,10 @@ packages: resolution: {integrity: sha512-/Pb0ctk3HTZ5xEL3BZ0hK1AqDSAUuRQitOmROPHhfUYEWpmTImwfD8vFDGADmMAX0JYgbcgxWoLFKtsWhcpuVA==} engines: {node: '>=18.0.0'} - has-bigints@1.1.0: - resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==} - engines: {node: '>= 0.4'} - has-flag@4.0.0: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} - has-property-descriptors@1.0.2: - resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} - - has-proto@1.2.0: - resolution: {integrity: sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==} - engines: {node: '>= 0.4'} - - has-symbols@1.1.0: - resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} - engines: {node: '>= 0.4'} - - has-tostringtag@1.0.2: - resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} - engines: {node: '>= 0.4'} - - hasown@2.0.2: - resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} - engines: {node: '>= 0.4'} - hast-util-to-html@9.0.5: resolution: {integrity: sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==} @@ -1911,70 +1737,18 @@ packages: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} engines: {node: '>=0.8.19'} - internal-slot@1.1.0: - resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} - engines: {node: '>= 0.4'} - - is-array-buffer@3.0.5: - resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==} - engines: {node: '>= 0.4'} - - is-async-function@2.1.1: - resolution: {integrity: sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==} - engines: {node: '>= 0.4'} - - is-bigint@1.1.0: - resolution: {integrity: sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==} - engines: {node: '>= 0.4'} - - is-boolean-object@1.2.2: - resolution: {integrity: sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==} - engines: {node: '>= 0.4'} - - is-callable@1.2.7: - resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} - engines: {node: '>= 0.4'} - - is-core-module@2.16.1: - resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} - engines: {node: '>= 0.4'} - - is-data-view@1.0.2: - resolution: {integrity: sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==} - engines: {node: '>= 0.4'} - - is-date-object@1.1.0: - resolution: {integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==} - engines: {node: '>= 0.4'} - is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} - is-finalizationregistry@1.1.1: - resolution: {integrity: sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==} - engines: {node: '>= 0.4'} - is-fullwidth-code-point@3.0.0: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} engines: {node: '>=8'} - is-generator-function@1.1.0: - resolution: {integrity: sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==} - engines: {node: '>= 0.4'} - is-glob@4.0.3: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} - is-map@2.0.3: - resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==} - engines: {node: '>= 0.4'} - - is-number-object@1.1.1: - resolution: {integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==} - engines: {node: '>= 0.4'} - is-number@7.0.0: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} @@ -1982,49 +1756,10 @@ packages: is-reference@3.0.3: resolution: {integrity: sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==} - is-regex@1.2.1: - resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} - engines: {node: '>= 0.4'} - - is-set@2.0.3: - resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==} - engines: {node: '>= 0.4'} - - is-shared-array-buffer@1.0.4: - resolution: {integrity: sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==} - engines: {node: '>= 0.4'} - - is-string@1.1.1: - resolution: {integrity: sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==} - engines: {node: '>= 0.4'} - - is-symbol@1.1.1: - resolution: {integrity: sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==} - engines: {node: '>= 0.4'} - - is-typed-array@1.1.15: - resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==} - engines: {node: '>= 0.4'} - - is-weakmap@2.0.2: - resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} - engines: {node: '>= 0.4'} - - is-weakref@1.1.1: - resolution: {integrity: sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==} - engines: {node: '>= 0.4'} - - is-weakset@2.0.4: - resolution: {integrity: sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==} - engines: {node: '>= 0.4'} - is-what@4.1.16: resolution: {integrity: sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==} engines: {node: '>=12.13'} - isarray@2.0.5: - resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} - isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} @@ -2033,17 +1768,10 @@ packages: peerDependencies: ws: '*' - iterator.prototype@1.1.5: - resolution: {integrity: sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==} - engines: {node: '>= 0.4'} - jackspeak@4.1.0: resolution: {integrity: sha512-9DDdhb5j6cpeitCbvLO7n7J4IxnbM6hoF6O1g4HQ5TfhvvKN8ywDM7668ZhMHRqVmxqhps/F6syWK2KcPxYlkw==} engines: {node: 20 || >=22} - js-tokens@4.0.0: - resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} - js-yaml@4.1.0: resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} hasBin: true @@ -2057,10 +1785,6 @@ packages: json-stable-stringify-without-jsonify@1.0.1: resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} - jsx-ast-utils@3.3.5: - resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} - engines: {node: '>=4.0'} - keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} @@ -2087,10 +1811,6 @@ packages: lodash.merge@4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} - loose-envify@1.4.0: - resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} - hasBin: true - loupe@3.1.3: resolution: {integrity: sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug==} @@ -2114,10 +1834,6 @@ packages: resolution: {integrity: sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==} hasBin: true - math-intrinsics@1.1.0: - resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} - engines: {node: '>= 0.4'} - mdast-util-to-hast@13.2.0: resolution: {integrity: sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==} @@ -2213,34 +1929,6 @@ packages: nostr-wasm@0.1.0: resolution: {integrity: sha512-78BTryCLcLYv96ONU8Ws3Q1JzjlAt+43pWQhIl86xZmWeegYCNLPml7yQ+gG3vR6V5h4XGj+TxO+SS5dsThQIA==} - object-assign@4.1.1: - resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} - engines: {node: '>=0.10.0'} - - object-inspect@1.13.4: - resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} - engines: {node: '>= 0.4'} - - object-keys@1.1.1: - resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} - engines: {node: '>= 0.4'} - - object.assign@4.1.7: - resolution: {integrity: sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==} - engines: {node: '>= 0.4'} - - object.entries@1.1.9: - resolution: {integrity: sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==} - engines: {node: '>= 0.4'} - - object.fromentries@2.0.8: - resolution: {integrity: sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==} - engines: {node: '>= 0.4'} - - object.values@1.2.1: - resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==} - engines: {node: '>= 0.4'} - oniguruma-to-es@3.1.1: resolution: {integrity: sha512-bUH8SDvPkH3ho3dvwJwfonjlQ4R80vjyvrU8YpxuROddv55vAEJrTuCuCVUhhsHbtlD9tGGbaNApGQckXhS8iQ==} @@ -2251,10 +1939,6 @@ packages: orderedmap@2.1.1: resolution: {integrity: sha512-TvAWxi0nDe1j/rtMcWcIj94+Ffe6n7zhow33h40SKxmsmozs6dz/e+EajymfoFcHd7sxNn8yHM8839uixMOV6g==} - own-keys@1.0.1: - resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==} - engines: {node: '>= 0.4'} - p-limit@3.1.0: resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} engines: {node: '>=10'} @@ -2278,9 +1962,6 @@ packages: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} - path-parse@1.0.7: - resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} - path-scurry@2.0.0: resolution: {integrity: sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==} engines: {node: 20 || >=22} @@ -2305,10 +1986,6 @@ packages: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} - possible-typed-array-names@1.1.0: - resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} - engines: {node: '>= 0.4'} - postcss@8.5.3: resolution: {integrity: sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==} engines: {node: ^10 || ^12 || >=14} @@ -2329,9 +2006,6 @@ packages: engines: {node: '>=14'} hasBin: true - prop-types@15.8.1: - resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} - property-information@7.0.0: resolution: {integrity: sha512-7D/qOz/+Y4X/rzSB6jKxKUsQnphO046ei8qxG59mtM3RG3DHgTK81HrxrmoDVINJb8NKT5ZsRbwHvQ6B68Iyhg==} @@ -2404,13 +2078,6 @@ packages: queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} - react-is@16.13.1: - resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} - - reflect.getprototypeof@1.0.10: - resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==} - engines: {node: '>= 0.4'} - regex-recursion@6.0.2: resolution: {integrity: sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==} @@ -2420,18 +2087,10 @@ packages: regex@6.0.1: resolution: {integrity: sha512-uorlqlzAKjKQZ5P+kTJr3eeJGSVroLKoHmquUj4zHWuR+hEyNqlXsSKlYYF5F4NI6nl7tWCs0apKJ0lmfsXAPA==} - regexp.prototype.flags@1.5.4: - resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==} - engines: {node: '>= 0.4'} - resolve-from@4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} - resolve@2.0.0-next.5: - resolution: {integrity: sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==} - hasBin: true - reusify@1.1.0: resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} @@ -2455,42 +2114,14 @@ packages: run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} - safe-array-concat@1.1.3: - resolution: {integrity: sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==} - engines: {node: '>=0.4'} - - safe-push-apply@1.0.0: - resolution: {integrity: sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==} - engines: {node: '>= 0.4'} - - safe-regex-test@1.1.0: - resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==} - engines: {node: '>= 0.4'} - search-insights@2.17.3: resolution: {integrity: sha512-RQPdCYTa8A68uM2jwxoY842xDhvx3E5LFL1LxvxCNMev4o5mLuokczhzjAgGwUZBAmOKZknArSxLKmXtIi2AxQ==} - semver@6.3.1: - resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} - hasBin: true - semver@7.7.1: resolution: {integrity: sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==} engines: {node: '>=10'} hasBin: true - set-function-length@1.2.2: - resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} - engines: {node: '>= 0.4'} - - set-function-name@2.0.2: - resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==} - engines: {node: '>= 0.4'} - - set-proto@1.0.0: - resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==} - engines: {node: '>= 0.4'} - shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} @@ -2502,22 +2133,6 @@ packages: shiki@2.5.0: resolution: {integrity: sha512-mI//trrsaiCIPsja5CNfsyNOqgAZUb6VpJA+340toL42UpzQlXpwRV9nch69X6gaUxrr9kaOOa6e3y3uAkGFxQ==} - side-channel-list@1.0.0: - resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} - engines: {node: '>= 0.4'} - - side-channel-map@1.0.1: - resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} - engines: {node: '>= 0.4'} - - side-channel-weakmap@1.0.2: - resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} - engines: {node: '>= 0.4'} - - side-channel@1.1.0: - resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} - engines: {node: '>= 0.4'} - siginfo@2.0.0: resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} @@ -2550,25 +2165,6 @@ packages: resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} engines: {node: '>=12'} - string.prototype.matchall@4.0.12: - resolution: {integrity: sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==} - engines: {node: '>= 0.4'} - - string.prototype.repeat@1.0.0: - resolution: {integrity: sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==} - - string.prototype.trim@1.2.10: - resolution: {integrity: sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==} - engines: {node: '>= 0.4'} - - string.prototype.trimend@1.0.9: - resolution: {integrity: sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==} - engines: {node: '>= 0.4'} - - string.prototype.trimstart@1.0.8: - resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} - engines: {node: '>= 0.4'} - stringify-entities@4.0.4: resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==} @@ -2592,10 +2188,6 @@ packages: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} - supports-preserve-symlinks-flag@1.0.0: - resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} - engines: {node: '>= 0.4'} - svelte@4.2.19: resolution: {integrity: sha512-IY1rnGr6izd10B0A8LqsBfmlT5OILVuZ7XsI0vdGPEvuonFV7NYEUK4dAkm9Zg2q0Um92kYjTpS1CAP3Nh/KWw==} engines: {node: '>=16'} @@ -2657,22 +2249,6 @@ packages: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} - typed-array-buffer@1.0.3: - resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} - engines: {node: '>= 0.4'} - - typed-array-byte-length@1.0.3: - resolution: {integrity: sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==} - engines: {node: '>= 0.4'} - - typed-array-byte-offset@1.0.4: - resolution: {integrity: sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==} - engines: {node: '>= 0.4'} - - typed-array-length@1.0.7: - 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'} @@ -2706,10 +2282,6 @@ packages: uc.micro@2.1.0: resolution: {integrity: sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==} - unbox-primitive@1.1.0: - resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==} - engines: {node: '>= 0.4'} - undici-types@6.20.0: resolution: {integrity: sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==} @@ -2872,22 +2444,6 @@ packages: resolution: {integrity: sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==} engines: {node: '>=12'} - which-boxed-primitive@1.1.1: - resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==} - engines: {node: '>= 0.4'} - - which-builtin-type@1.2.1: - resolution: {integrity: sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==} - engines: {node: '>= 0.4'} - - which-collection@1.0.2: - resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==} - engines: {node: '>= 0.4'} - - which-typed-array@1.1.19: - resolution: {integrity: sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==} - engines: {node: '>= 0.4'} - which@2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} engines: {node: '>= 8'} @@ -3905,69 +3461,8 @@ snapshots: aria-query@5.3.2: {} - array-buffer-byte-length@1.0.2: - dependencies: - call-bound: 1.0.4 - is-array-buffer: 3.0.5 - - array-includes@3.1.8: - dependencies: - call-bind: 1.0.8 - define-properties: 1.2.1 - es-abstract: 1.23.9 - es-object-atoms: 1.1.1 - get-intrinsic: 1.3.0 - is-string: 1.1.1 - - array.prototype.findlast@1.2.5: - dependencies: - call-bind: 1.0.8 - define-properties: 1.2.1 - es-abstract: 1.23.9 - es-errors: 1.3.0 - es-object-atoms: 1.1.1 - es-shim-unscopables: 1.1.0 - - array.prototype.flat@1.3.3: - dependencies: - call-bind: 1.0.8 - define-properties: 1.2.1 - es-abstract: 1.23.9 - es-shim-unscopables: 1.1.0 - - array.prototype.flatmap@1.3.3: - dependencies: - call-bind: 1.0.8 - define-properties: 1.2.1 - es-abstract: 1.23.9 - es-shim-unscopables: 1.1.0 - - array.prototype.tosorted@1.1.4: - dependencies: - call-bind: 1.0.8 - define-properties: 1.2.1 - es-abstract: 1.23.9 - es-errors: 1.3.0 - es-shim-unscopables: 1.1.0 - - arraybuffer.prototype.slice@1.0.4: - dependencies: - array-buffer-byte-length: 1.0.2 - call-bind: 1.0.8 - define-properties: 1.2.1 - es-abstract: 1.23.9 - es-errors: 1.3.0 - get-intrinsic: 1.3.0 - is-array-buffer: 3.0.5 - assertion-error@2.0.1: {} - async-function@1.0.0: {} - - available-typed-arrays@1.0.7: - dependencies: - possible-typed-array-names: 1.1.0 - axobject-query@4.1.0: {} balanced-match@1.0.2: {} @@ -3989,23 +3484,6 @@ snapshots: cac@6.7.14: {} - call-bind-apply-helpers@1.0.2: - dependencies: - es-errors: 1.3.0 - function-bind: 1.1.2 - - call-bind@1.0.8: - dependencies: - call-bind-apply-helpers: 1.0.2 - es-define-property: 1.0.1 - get-intrinsic: 1.3.0 - set-function-length: 1.2.2 - - call-bound@1.0.4: - dependencies: - call-bind-apply-helpers: 1.0.2 - get-intrinsic: 1.3.0 - callsites@3.1.0: {} ccount@2.0.1: {} @@ -4066,24 +3544,6 @@ snapshots: csstype@3.1.3: {} - data-view-buffer@1.0.2: - dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - is-data-view: 1.0.2 - - data-view-byte-length@1.0.2: - dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - is-data-view: 1.0.2 - - data-view-byte-offset@1.0.1: - dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - is-data-view: 1.0.2 - debug@4.4.0: dependencies: ms: 2.1.3 @@ -4092,34 +3552,12 @@ snapshots: deep-is@0.1.4: {} - define-data-property@1.1.4: - dependencies: - es-define-property: 1.0.1 - es-errors: 1.3.0 - gopd: 1.2.0 - - define-properties@1.2.1: - dependencies: - define-data-property: 1.1.4 - has-property-descriptors: 1.0.2 - object-keys: 1.1.1 - dequal@2.0.3: {} devlop@1.1.0: dependencies: dequal: 2.0.3 - doctrine@2.1.0: - dependencies: - esutils: 2.0.3 - - dunder-proto@1.0.1: - dependencies: - call-bind-apply-helpers: 1.0.2 - es-errors: 1.3.0 - gopd: 1.2.0 - eastasianwidth@0.2.0: {} emoji-regex-xs@1.0.0: {} @@ -4130,106 +3568,8 @@ snapshots: entities@4.5.0: {} - es-abstract@1.23.9: - dependencies: - array-buffer-byte-length: 1.0.2 - arraybuffer.prototype.slice: 1.0.4 - available-typed-arrays: 1.0.7 - call-bind: 1.0.8 - call-bound: 1.0.4 - data-view-buffer: 1.0.2 - data-view-byte-length: 1.0.2 - data-view-byte-offset: 1.0.1 - es-define-property: 1.0.1 - es-errors: 1.3.0 - es-object-atoms: 1.1.1 - es-set-tostringtag: 2.1.0 - es-to-primitive: 1.3.0 - function.prototype.name: 1.1.8 - get-intrinsic: 1.3.0 - get-proto: 1.0.1 - get-symbol-description: 1.1.0 - globalthis: 1.0.4 - gopd: 1.2.0 - has-property-descriptors: 1.0.2 - has-proto: 1.2.0 - has-symbols: 1.1.0 - hasown: 2.0.2 - internal-slot: 1.1.0 - is-array-buffer: 3.0.5 - is-callable: 1.2.7 - is-data-view: 1.0.2 - is-regex: 1.2.1 - is-shared-array-buffer: 1.0.4 - is-string: 1.1.1 - is-typed-array: 1.1.15 - is-weakref: 1.1.1 - math-intrinsics: 1.1.0 - object-inspect: 1.13.4 - object-keys: 1.1.1 - object.assign: 4.1.7 - own-keys: 1.0.1 - regexp.prototype.flags: 1.5.4 - safe-array-concat: 1.1.3 - safe-push-apply: 1.0.0 - safe-regex-test: 1.1.0 - set-proto: 1.0.0 - string.prototype.trim: 1.2.10 - string.prototype.trimend: 1.0.9 - string.prototype.trimstart: 1.0.8 - typed-array-buffer: 1.0.3 - typed-array-byte-length: 1.0.3 - typed-array-byte-offset: 1.0.4 - typed-array-length: 1.0.7 - unbox-primitive: 1.1.0 - which-typed-array: 1.1.19 - - es-define-property@1.0.1: {} - - es-errors@1.3.0: {} - - es-iterator-helpers@1.2.1: - dependencies: - call-bind: 1.0.8 - call-bound: 1.0.4 - define-properties: 1.2.1 - es-abstract: 1.23.9 - es-errors: 1.3.0 - es-set-tostringtag: 2.1.0 - function-bind: 1.1.2 - get-intrinsic: 1.3.0 - globalthis: 1.0.4 - gopd: 1.2.0 - has-property-descriptors: 1.0.2 - has-proto: 1.2.0 - has-symbols: 1.1.0 - internal-slot: 1.1.0 - iterator.prototype: 1.1.5 - safe-array-concat: 1.1.3 - es-module-lexer@1.6.0: {} - es-object-atoms@1.1.1: - dependencies: - es-errors: 1.3.0 - - es-set-tostringtag@2.1.0: - dependencies: - es-errors: 1.3.0 - get-intrinsic: 1.3.0 - has-tostringtag: 1.0.2 - hasown: 2.0.2 - - es-shim-unscopables@1.1.0: - dependencies: - hasown: 2.0.2 - - es-to-primitive@1.3.0: - dependencies: - is-callable: 1.2.7 - is-date-object: 1.1.0 - is-symbol: 1.1.1 - esbuild@0.21.5: optionalDependencies: '@esbuild/aix-ppc64': 0.21.5 @@ -4299,28 +3639,6 @@ snapshots: optionalDependencies: eslint-config-prettier: 10.1.1(eslint@9.23.0) - eslint-plugin-react@7.37.4(eslint@9.23.0): - dependencies: - array-includes: 3.1.8 - array.prototype.findlast: 1.2.5 - array.prototype.flatmap: 1.3.3 - array.prototype.tosorted: 1.1.4 - doctrine: 2.1.0 - es-iterator-helpers: 1.2.1 - eslint: 9.23.0 - estraverse: 5.3.0 - hasown: 2.0.2 - jsx-ast-utils: 3.3.5 - minimatch: 3.1.2 - object.entries: 1.1.9 - object.fromentries: 2.0.8 - object.values: 1.2.1 - prop-types: 15.8.1 - resolve: 2.0.0-next.5 - semver: 6.3.1 - string.prototype.matchall: 4.0.12 - string.prototype.repeat: 1.0.0 - eslint-scope@8.3.0: dependencies: esrecurse: 4.3.0 @@ -4444,10 +3762,6 @@ snapshots: dependencies: tabbable: 6.2.0 - for-each@0.3.5: - dependencies: - is-callable: 1.2.7 - foreground-child@3.3.1: dependencies: cross-spawn: 7.0.6 @@ -4456,45 +3770,8 @@ snapshots: fsevents@2.3.3: optional: true - function-bind@1.1.2: {} - - function.prototype.name@1.1.8: - dependencies: - call-bind: 1.0.8 - call-bound: 1.0.4 - define-properties: 1.2.1 - functions-have-names: 1.2.3 - hasown: 2.0.2 - is-callable: 1.2.7 - - functions-have-names@1.2.3: {} - fuse.js@7.1.0: {} - get-intrinsic@1.3.0: - dependencies: - call-bind-apply-helpers: 1.0.2 - es-define-property: 1.0.1 - es-errors: 1.3.0 - es-object-atoms: 1.1.1 - function-bind: 1.1.2 - get-proto: 1.0.1 - gopd: 1.2.0 - has-symbols: 1.1.0 - hasown: 2.0.2 - math-intrinsics: 1.1.0 - - get-proto@1.0.1: - dependencies: - dunder-proto: 1.0.1 - es-object-atoms: 1.1.1 - - get-symbol-description@1.1.0: - dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - get-intrinsic: 1.3.0 - glob-parent@5.1.2: dependencies: is-glob: 4.0.3 @@ -4516,13 +3793,6 @@ snapshots: globals@16.0.0: {} - globalthis@1.0.4: - dependencies: - define-properties: 1.2.1 - gopd: 1.2.0 - - gopd@1.2.0: {} - graphemer@1.4.0: {} happy-dom@17.4.4: @@ -4530,28 +3800,8 @@ snapshots: webidl-conversions: 7.0.0 whatwg-mimetype: 3.0.0 - has-bigints@1.1.0: {} - has-flag@4.0.0: {} - has-property-descriptors@1.0.2: - dependencies: - es-define-property: 1.0.1 - - has-proto@1.2.0: - dependencies: - dunder-proto: 1.0.1 - - has-symbols@1.1.0: {} - - has-tostringtag@1.0.2: - dependencies: - has-symbols: 1.1.0 - - hasown@2.0.2: - dependencies: - function-bind: 1.1.2 - hast-util-to-html@9.0.5: dependencies: '@types/hast': 3.0.4 @@ -4585,148 +3835,32 @@ snapshots: imurmurhash@0.1.4: {} - internal-slot@1.1.0: - dependencies: - es-errors: 1.3.0 - hasown: 2.0.2 - side-channel: 1.1.0 - - is-array-buffer@3.0.5: - dependencies: - call-bind: 1.0.8 - call-bound: 1.0.4 - get-intrinsic: 1.3.0 - - is-async-function@2.1.1: - dependencies: - async-function: 1.0.0 - call-bound: 1.0.4 - get-proto: 1.0.1 - has-tostringtag: 1.0.2 - safe-regex-test: 1.1.0 - - is-bigint@1.1.0: - dependencies: - has-bigints: 1.1.0 - - is-boolean-object@1.2.2: - dependencies: - call-bound: 1.0.4 - has-tostringtag: 1.0.2 - - is-callable@1.2.7: {} - - is-core-module@2.16.1: - dependencies: - hasown: 2.0.2 - - is-data-view@1.0.2: - dependencies: - call-bound: 1.0.4 - get-intrinsic: 1.3.0 - is-typed-array: 1.1.15 - - is-date-object@1.1.0: - dependencies: - call-bound: 1.0.4 - has-tostringtag: 1.0.2 - is-extglob@2.1.1: {} - is-finalizationregistry@1.1.1: - dependencies: - call-bound: 1.0.4 - is-fullwidth-code-point@3.0.0: {} - is-generator-function@1.1.0: - dependencies: - call-bound: 1.0.4 - get-proto: 1.0.1 - has-tostringtag: 1.0.2 - safe-regex-test: 1.1.0 - is-glob@4.0.3: dependencies: is-extglob: 2.1.1 - is-map@2.0.3: {} - - is-number-object@1.1.1: - dependencies: - call-bound: 1.0.4 - has-tostringtag: 1.0.2 - is-number@7.0.0: {} is-reference@3.0.3: dependencies: '@types/estree': 1.0.7 - is-regex@1.2.1: - dependencies: - call-bound: 1.0.4 - gopd: 1.2.0 - has-tostringtag: 1.0.2 - hasown: 2.0.2 - - is-set@2.0.3: {} - - is-shared-array-buffer@1.0.4: - dependencies: - call-bound: 1.0.4 - - is-string@1.1.1: - dependencies: - call-bound: 1.0.4 - has-tostringtag: 1.0.2 - - is-symbol@1.1.1: - dependencies: - call-bound: 1.0.4 - has-symbols: 1.1.0 - safe-regex-test: 1.1.0 - - is-typed-array@1.1.15: - dependencies: - which-typed-array: 1.1.19 - - is-weakmap@2.0.2: {} - - is-weakref@1.1.1: - dependencies: - call-bound: 1.0.4 - - is-weakset@2.0.4: - dependencies: - call-bound: 1.0.4 - get-intrinsic: 1.3.0 - is-what@4.1.16: {} - isarray@2.0.5: {} - isexe@2.0.0: {} isomorphic-ws@5.0.0(ws@8.18.1): dependencies: ws: 8.18.1 - iterator.prototype@1.1.5: - dependencies: - define-data-property: 1.1.4 - es-object-atoms: 1.1.1 - get-intrinsic: 1.3.0 - get-proto: 1.0.1 - has-symbols: 1.1.0 - set-function-name: 2.0.2 - jackspeak@4.1.0: dependencies: '@isaacs/cliui': 8.0.2 - js-tokens@4.0.0: {} - js-yaml@4.1.0: dependencies: argparse: 2.0.1 @@ -4737,13 +3871,6 @@ snapshots: json-stable-stringify-without-jsonify@1.0.1: {} - jsx-ast-utils@3.3.5: - dependencies: - array-includes: 3.1.8 - array.prototype.flat: 1.3.3 - object.assign: 4.1.7 - object.values: 1.2.1 - keyv@4.5.4: dependencies: json-buffer: 3.0.1 @@ -4771,10 +3898,6 @@ snapshots: lodash.merge@4.6.2: {} - loose-envify@1.4.0: - dependencies: - js-tokens: 4.0.0 - loupe@3.1.3: {} lru-cache@11.1.0: {} @@ -4798,8 +3921,6 @@ snapshots: punycode.js: 2.3.1 uc.micro: 2.1.0 - math-intrinsics@1.1.0: {} - mdast-util-to-hast@13.2.0: dependencies: '@types/hast': 3.0.4 @@ -4896,42 +4017,6 @@ snapshots: nostr-wasm@0.1.0: {} - object-assign@4.1.1: {} - - object-inspect@1.13.4: {} - - object-keys@1.1.1: {} - - object.assign@4.1.7: - dependencies: - call-bind: 1.0.8 - call-bound: 1.0.4 - define-properties: 1.2.1 - es-object-atoms: 1.1.1 - has-symbols: 1.1.0 - object-keys: 1.1.1 - - object.entries@1.1.9: - dependencies: - call-bind: 1.0.8 - call-bound: 1.0.4 - define-properties: 1.2.1 - es-object-atoms: 1.1.1 - - object.fromentries@2.0.8: - dependencies: - call-bind: 1.0.8 - define-properties: 1.2.1 - es-abstract: 1.23.9 - es-object-atoms: 1.1.1 - - object.values@1.2.1: - dependencies: - call-bind: 1.0.8 - call-bound: 1.0.4 - define-properties: 1.2.1 - es-object-atoms: 1.1.1 - oniguruma-to-es@3.1.1: dependencies: emoji-regex-xs: 1.0.0 @@ -4949,12 +4034,6 @@ snapshots: orderedmap@2.1.1: {} - own-keys@1.0.1: - dependencies: - get-intrinsic: 1.3.0 - object-keys: 1.1.1 - safe-push-apply: 1.0.0 - p-limit@3.1.0: dependencies: yocto-queue: 0.1.0 @@ -4973,8 +4052,6 @@ snapshots: path-key@3.1.1: {} - path-parse@1.0.7: {} - path-scurry@2.0.0: dependencies: lru-cache: 11.1.0 @@ -4996,8 +4073,6 @@ snapshots: picomatch@2.3.1: {} - possible-typed-array-names@1.1.0: {} - postcss@8.5.3: dependencies: nanoid: 3.3.11 @@ -5014,12 +4089,6 @@ snapshots: prettier@3.5.3: {} - prop-types@15.8.1: - dependencies: - loose-envify: 1.4.0 - object-assign: 4.1.1 - react-is: 16.13.1 - property-information@7.0.0: {} prosemirror-changeset@2.2.1: @@ -5131,19 +4200,6 @@ snapshots: queue-microtask@1.2.3: {} - react-is@16.13.1: {} - - reflect.getprototypeof@1.0.10: - dependencies: - call-bind: 1.0.8 - define-properties: 1.2.1 - es-abstract: 1.23.9 - es-errors: 1.3.0 - es-object-atoms: 1.1.1 - get-intrinsic: 1.3.0 - get-proto: 1.0.1 - which-builtin-type: 1.2.1 - regex-recursion@6.0.2: dependencies: regex-utilities: 2.3.0 @@ -5154,23 +4210,8 @@ snapshots: dependencies: regex-utilities: 2.3.0 - regexp.prototype.flags@1.5.4: - dependencies: - call-bind: 1.0.8 - define-properties: 1.2.1 - es-errors: 1.3.0 - get-proto: 1.0.1 - gopd: 1.2.0 - set-function-name: 2.0.2 - resolve-from@4.0.0: {} - resolve@2.0.0-next.5: - dependencies: - is-core-module: 2.16.1 - path-parse: 1.0.7 - supports-preserve-symlinks-flag: 1.0.0 - reusify@1.1.0: {} rfdc@1.4.1: {} @@ -5212,53 +4253,10 @@ snapshots: dependencies: queue-microtask: 1.2.3 - safe-array-concat@1.1.3: - dependencies: - call-bind: 1.0.8 - call-bound: 1.0.4 - get-intrinsic: 1.3.0 - has-symbols: 1.1.0 - isarray: 2.0.5 - - safe-push-apply@1.0.0: - dependencies: - es-errors: 1.3.0 - isarray: 2.0.5 - - safe-regex-test@1.1.0: - dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - is-regex: 1.2.1 - search-insights@2.17.3: {} - semver@6.3.1: {} - semver@7.7.1: {} - set-function-length@1.2.2: - dependencies: - define-data-property: 1.1.4 - es-errors: 1.3.0 - function-bind: 1.1.2 - get-intrinsic: 1.3.0 - gopd: 1.2.0 - has-property-descriptors: 1.0.2 - - set-function-name@2.0.2: - dependencies: - define-data-property: 1.1.4 - es-errors: 1.3.0 - functions-have-names: 1.2.3 - has-property-descriptors: 1.0.2 - - set-proto@1.0.0: - dependencies: - dunder-proto: 1.0.1 - es-errors: 1.3.0 - es-object-atoms: 1.1.1 - shebang-command@2.0.0: dependencies: shebang-regex: 3.0.0 @@ -5276,34 +4274,6 @@ snapshots: '@shikijs/vscode-textmate': 10.0.2 '@types/hast': 3.0.4 - side-channel-list@1.0.0: - dependencies: - es-errors: 1.3.0 - object-inspect: 1.13.4 - - side-channel-map@1.0.1: - dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - get-intrinsic: 1.3.0 - object-inspect: 1.13.4 - - side-channel-weakmap@1.0.2: - dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - get-intrinsic: 1.3.0 - object-inspect: 1.13.4 - side-channel-map: 1.0.1 - - side-channel@1.1.0: - dependencies: - es-errors: 1.3.0 - object-inspect: 1.13.4 - side-channel-list: 1.0.0 - side-channel-map: 1.0.1 - side-channel-weakmap: 1.0.2 - siginfo@2.0.0: {} signal-exit@4.1.0: {} @@ -5330,50 +4300,6 @@ snapshots: emoji-regex: 9.2.2 strip-ansi: 7.1.0 - string.prototype.matchall@4.0.12: - dependencies: - call-bind: 1.0.8 - call-bound: 1.0.4 - define-properties: 1.2.1 - es-abstract: 1.23.9 - es-errors: 1.3.0 - es-object-atoms: 1.1.1 - get-intrinsic: 1.3.0 - gopd: 1.2.0 - has-symbols: 1.1.0 - internal-slot: 1.1.0 - regexp.prototype.flags: 1.5.4 - set-function-name: 2.0.2 - side-channel: 1.1.0 - - string.prototype.repeat@1.0.0: - dependencies: - define-properties: 1.2.1 - es-abstract: 1.23.9 - - string.prototype.trim@1.2.10: - dependencies: - call-bind: 1.0.8 - call-bound: 1.0.4 - define-data-property: 1.1.4 - define-properties: 1.2.1 - es-abstract: 1.23.9 - es-object-atoms: 1.1.1 - has-property-descriptors: 1.0.2 - - string.prototype.trimend@1.0.9: - dependencies: - call-bind: 1.0.8 - call-bound: 1.0.4 - define-properties: 1.2.1 - es-object-atoms: 1.1.1 - - string.prototype.trimstart@1.0.8: - dependencies: - call-bind: 1.0.8 - define-properties: 1.2.1 - es-object-atoms: 1.1.1 - stringify-entities@4.0.4: dependencies: character-entities-html4: 2.1.0 @@ -5397,8 +4323,6 @@ snapshots: dependencies: has-flag: 4.0.0 - supports-preserve-symlinks-flag@1.0.0: {} - svelte@4.2.19: dependencies: '@ampproject/remapping': 2.3.0 @@ -5463,39 +4387,6 @@ snapshots: dependencies: prelude-ls: 1.2.1 - typed-array-buffer@1.0.3: - dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - is-typed-array: 1.1.15 - - typed-array-byte-length@1.0.3: - dependencies: - call-bind: 1.0.8 - for-each: 0.3.5 - gopd: 1.2.0 - has-proto: 1.2.0 - is-typed-array: 1.1.15 - - typed-array-byte-offset@1.0.4: - dependencies: - available-typed-arrays: 1.0.7 - call-bind: 1.0.8 - for-each: 0.3.5 - gopd: 1.2.0 - has-proto: 1.2.0 - is-typed-array: 1.1.15 - reflect.getprototypeof: 1.0.10 - - typed-array-length@1.0.7: - dependencies: - call-bind: 1.0.8 - for-each: 0.3.5 - gopd: 1.2.0 - is-typed-array: 1.1.15 - 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) @@ -5527,13 +4418,6 @@ snapshots: uc.micro@2.1.0: {} - unbox-primitive@1.1.0: - dependencies: - call-bound: 1.0.4 - has-bigints: 1.1.0 - has-symbols: 1.1.0 - which-boxed-primitive: 1.1.1 - undici-types@6.20.0: {} unist-util-is@6.0.0: @@ -5717,47 +4601,6 @@ snapshots: whatwg-mimetype@3.0.0: {} - which-boxed-primitive@1.1.1: - dependencies: - is-bigint: 1.1.0 - is-boolean-object: 1.2.2 - is-number-object: 1.1.1 - is-string: 1.1.1 - is-symbol: 1.1.1 - - which-builtin-type@1.2.1: - dependencies: - call-bound: 1.0.4 - function.prototype.name: 1.1.8 - has-tostringtag: 1.0.2 - is-async-function: 2.1.1 - is-date-object: 1.1.0 - is-finalizationregistry: 1.1.1 - is-generator-function: 1.1.0 - is-regex: 1.2.1 - is-weakref: 1.1.1 - isarray: 2.0.5 - which-boxed-primitive: 1.1.1 - which-collection: 1.0.2 - which-typed-array: 1.1.19 - - which-collection@1.0.2: - dependencies: - is-map: 2.0.3 - is-set: 2.0.3 - is-weakmap: 2.0.2 - is-weakset: 2.0.4 - - which-typed-array@1.1.19: - dependencies: - available-typed-arrays: 1.0.7 - call-bind: 1.0.8 - call-bound: 1.0.4 - for-each: 0.3.5 - get-proto: 1.0.1 - gopd: 1.2.0 - has-tostringtag: 1.0.2 - which@2.0.2: dependencies: isexe: 2.0.0