UNPKG

rx-nostr

Version:

A library based on RxJS, which allows Nostr applications to easily communicate with relays.

1 lines 352 kB
{"version":3,"file":"rx-nostr.umd.cjs","sources":["../src/error.ts","../src/utils/config.ts","../src/nostr/event.ts","../src/utils/inline-throw.ts","../src/config/signer.ts","../src/config/verifier.ts","../src/config/config.ts","../src/lazy-filter.ts","../src/nostr/nip11.ts","../src/utils/inline-try.ts","../src/utils/normalize-url.ts","../src/utils/url-map.ts","../src/nip11.ts","../../../node_modules/@noble/hashes/utils.js","../../../node_modules/@scure/base/index.js","../src/nostr/bech32.ts","../src/nostr/filter.ts","../src/nostr/nip40.ts","../node_modules/tslib/tslib.es6.mjs","../node_modules/rxjs/dist/esm5/internal/util/isFunction.js","../node_modules/rxjs/dist/esm5/internal/util/createErrorClass.js","../node_modules/rxjs/dist/esm5/internal/util/UnsubscriptionError.js","../node_modules/rxjs/dist/esm5/internal/util/arrRemove.js","../node_modules/rxjs/dist/esm5/internal/Subscription.js","../node_modules/rxjs/dist/esm5/internal/config.js","../node_modules/rxjs/dist/esm5/internal/scheduler/timeoutProvider.js","../node_modules/rxjs/dist/esm5/internal/util/reportUnhandledError.js","../node_modules/rxjs/dist/esm5/internal/util/noop.js","../node_modules/rxjs/dist/esm5/internal/util/errorContext.js","../node_modules/rxjs/dist/esm5/internal/Subscriber.js","../node_modules/rxjs/dist/esm5/internal/symbol/observable.js","../node_modules/rxjs/dist/esm5/internal/util/identity.js","../node_modules/rxjs/dist/esm5/internal/util/pipe.js","../node_modules/rxjs/dist/esm5/internal/Observable.js","../node_modules/rxjs/dist/esm5/internal/util/lift.js","../node_modules/rxjs/dist/esm5/internal/operators/OperatorSubscriber.js","../node_modules/rxjs/dist/esm5/internal/util/ObjectUnsubscribedError.js","../node_modules/rxjs/dist/esm5/internal/Subject.js","../node_modules/rxjs/dist/esm5/internal/BehaviorSubject.js","../node_modules/rxjs/dist/esm5/internal/scheduler/dateTimestampProvider.js","../node_modules/rxjs/dist/esm5/internal/scheduler/Action.js","../node_modules/rxjs/dist/esm5/internal/scheduler/intervalProvider.js","../node_modules/rxjs/dist/esm5/internal/scheduler/AsyncAction.js","../node_modules/rxjs/dist/esm5/internal/Scheduler.js","../node_modules/rxjs/dist/esm5/internal/scheduler/AsyncScheduler.js","../node_modules/rxjs/dist/esm5/internal/scheduler/async.js","../node_modules/rxjs/dist/esm5/internal/observable/empty.js","../node_modules/rxjs/dist/esm5/internal/util/isScheduler.js","../node_modules/rxjs/dist/esm5/internal/util/args.js","../node_modules/rxjs/dist/esm5/internal/util/isArrayLike.js","../node_modules/rxjs/dist/esm5/internal/util/isPromise.js","../node_modules/rxjs/dist/esm5/internal/util/isInteropObservable.js","../node_modules/rxjs/dist/esm5/internal/util/isAsyncIterable.js","../node_modules/rxjs/dist/esm5/internal/util/throwUnobservableError.js","../node_modules/rxjs/dist/esm5/internal/symbol/iterator.js","../node_modules/rxjs/dist/esm5/internal/util/isIterable.js","../node_modules/rxjs/dist/esm5/internal/util/isReadableStreamLike.js","../node_modules/rxjs/dist/esm5/internal/observable/innerFrom.js","../node_modules/rxjs/dist/esm5/internal/util/executeSchedule.js","../node_modules/rxjs/dist/esm5/internal/operators/observeOn.js","../node_modules/rxjs/dist/esm5/internal/operators/subscribeOn.js","../node_modules/rxjs/dist/esm5/internal/scheduled/scheduleObservable.js","../node_modules/rxjs/dist/esm5/internal/scheduled/schedulePromise.js","../node_modules/rxjs/dist/esm5/internal/scheduled/scheduleArray.js","../node_modules/rxjs/dist/esm5/internal/scheduled/scheduleIterable.js","../node_modules/rxjs/dist/esm5/internal/scheduled/scheduleAsyncIterable.js","../node_modules/rxjs/dist/esm5/internal/scheduled/scheduleReadableStreamLike.js","../node_modules/rxjs/dist/esm5/internal/scheduled/scheduled.js","../node_modules/rxjs/dist/esm5/internal/observable/from.js","../node_modules/rxjs/dist/esm5/internal/observable/of.js","../node_modules/rxjs/dist/esm5/internal/util/EmptyError.js","../node_modules/rxjs/dist/esm5/internal/firstValueFrom.js","../node_modules/rxjs/dist/esm5/internal/util/isDate.js","../node_modules/rxjs/dist/esm5/internal/operators/timeout.js","../node_modules/rxjs/dist/esm5/internal/operators/map.js","../node_modules/rxjs/dist/esm5/internal/util/mapOneOrManyArgs.js","../node_modules/rxjs/dist/esm5/internal/util/argsArgArrayOrObject.js","../node_modules/rxjs/dist/esm5/internal/util/createObject.js","../node_modules/rxjs/dist/esm5/internal/observable/combineLatest.js","../node_modules/rxjs/dist/esm5/internal/operators/mergeInternals.js","../node_modules/rxjs/dist/esm5/internal/operators/mergeMap.js","../node_modules/rxjs/dist/esm5/internal/operators/mergeAll.js","../node_modules/rxjs/dist/esm5/internal/operators/concatAll.js","../node_modules/rxjs/dist/esm5/internal/observable/concat.js","../node_modules/rxjs/dist/esm5/internal/observable/timer.js","../node_modules/rxjs/dist/esm5/internal/observable/merge.js","../node_modules/rxjs/dist/esm5/internal/operators/filter.js","../node_modules/rxjs/dist/esm5/internal/operators/catchError.js","../node_modules/rxjs/dist/esm5/internal/operators/scanInternals.js","../node_modules/rxjs/dist/esm5/internal/operators/defaultIfEmpty.js","../node_modules/rxjs/dist/esm5/internal/operators/take.js","../node_modules/rxjs/dist/esm5/internal/operators/ignoreElements.js","../node_modules/rxjs/dist/esm5/internal/operators/mapTo.js","../node_modules/rxjs/dist/esm5/internal/operators/delayWhen.js","../node_modules/rxjs/dist/esm5/internal/operators/delay.js","../node_modules/rxjs/dist/esm5/internal/operators/distinct.js","../node_modules/rxjs/dist/esm5/internal/operators/distinctUntilChanged.js","../node_modules/rxjs/dist/esm5/internal/operators/throwIfEmpty.js","../node_modules/rxjs/dist/esm5/internal/operators/finalize.js","../node_modules/rxjs/dist/esm5/internal/operators/first.js","../node_modules/rxjs/dist/esm5/internal/operators/groupBy.js","../node_modules/rxjs/dist/esm5/internal/operators/scan.js","../node_modules/rxjs/dist/esm5/internal/operators/switchMap.js","../node_modules/rxjs/dist/esm5/internal/operators/switchAll.js","../node_modules/rxjs/dist/esm5/internal/operators/takeUntil.js","../node_modules/rxjs/dist/esm5/internal/operators/takeWhile.js","../node_modules/rxjs/dist/esm5/internal/operators/tap.js","../src/operator.ts","../src/connection/auth.ts","../src/connection/utils.ts","../src/connection/publish.ts","../src/websocket.ts","../src/connection/relay.ts","../src/connection/subscribe.ts","../src/connection/connection.ts","../src/utils/array-operation.ts","../src/rx-nostr/utils.ts","../src/rx-nostr/rx-nostr.ts","../src/rx-nostr/rx-req.ts","../src/index.ts"],"sourcesContent":["export abstract class RxNostrError extends Error {}\n\n/**\n * This is thrown when WebSocket connection is closed unexpectedly.\n * You may see them in a stream made by `rxNostr.createAllErrorObservable()`.\n */\nexport class RxNostrWebSocketError extends RxNostrError {\n constructor(public code?: number) {\n super(\n `RxNostrWebSocketError: WebSocket was closed with code ${code} by relay.`,\n );\n this.name = \"RxNostrWebSocketError\";\n }\n}\n\n/**\n * This is usually thrown when rx-nostr is used incorrectly (or possibly rx-nostr has a bug).\n * Please fix your program according to the message.\n *\n * Normally, you should not catch the exception.\n */\nexport class RxNostrInvalidUsageError extends RxNostrError {\n constructor(message: string) {\n super(`RxNostrInvalidUsageError: ${message}`);\n this.name = \"RxNostrInvalidUsageError\";\n }\n}\n\n/**\n * This is usually thrown when rx-nostr is used properly\n * but an error occurs due to external environmental causes.\n */\nexport class RxNostrEnvironmentError extends RxNostrError {\n constructor(message: string) {\n super(`RxNostrEnvironmentError: ${message}`);\n this.name = \"RxNostrEnvironmentError\";\n }\n}\n\n/**\n * This is thrown only by a bug inside rx-nostr.\n *\n * Normally, you should not catch the exception.\n */\nexport class RxNostrLogicError extends RxNostrError {\n constructor() {\n super(\n \"RxNostrLogicError: This is rx-nostr's internal bug. Please report to the author of the library.\",\n );\n this.name = \"RxNostrLogicError\";\n }\n}\n\n/**\n * This is thrown when you attempt to access a disposed rx-nostr's resource.\n *\n * Normally, you should not catch the exception.\n */\nexport class RxNostrAlreadyDisposedError extends RxNostrError {\n constructor() {\n super(\n \"RxNostrAlreadyDisposedError: Attempted to access a disposed resource.\",\n );\n this.name = \"RxNostrAlreadyDisposedError\";\n }\n}\n","import { RxNostrLogicError } from \"../error.js\";\nimport { EMPTY, Empty } from \"./empty.js\";\nimport type { OptionalPart, RequiredPart } from \"./types.js\";\n\ntype FilledConfig<C, D> = RequiredPart<C> & {\n [K in keyof OptionalPart<C>]: K extends keyof D ? NonNullable<C[K]> : C[K];\n};\n\nexport const config = <C>() => ({\n default: <const D extends Partial<OptionalPart<C>>>(factory: () => D) => {\n const defaults = factory();\n let inputs: C | Empty = EMPTY;\n\n return {\n set(config: C) {\n inputs = config;\n },\n get<const K extends keyof FilledConfig<C, D>>(\n key: K,\n ): FilledConfig<C, D>[K] {\n if (inputs === EMPTY) {\n throw new RxNostrLogicError();\n }\n\n return (inputs[key] ?? defaults[key]) as FilledConfig<C, D>[K];\n },\n };\n },\n});\n\nexport const fill = <\n C,\n const D extends Partial<OptionalPart<C>> = Partial<OptionalPart<C>>,\n>(\n config: C,\n defaults: D,\n): FilledConfig<C, D> =>\n ({\n ...defaults,\n ...config,\n }) as FilledConfig<C, D>;\n","import * as Nostr from \"nostr-typedef\";\n\nexport function ensureEventFields(\n event: Partial<Nostr.Event>,\n): event is Nostr.Event {\n if (typeof event.id !== \"string\") return false;\n if (typeof event.sig !== \"string\") return false;\n if (typeof event.kind !== \"number\") return false;\n if (typeof event.pubkey !== \"string\") return false;\n if (typeof event.content !== \"string\") return false;\n if (typeof event.created_at !== \"number\") return false;\n\n if (!Array.isArray(event.tags)) return false;\n for (let i = 0; i < event.tags.length; i++) {\n const tag = event.tags[i];\n if (!Array.isArray(tag)) return false;\n for (let j = 0; j < tag.length; j++) {\n if (typeof tag[j] === \"object\") return false;\n }\n }\n\n return true;\n}\n\n/** Return an event that has earlier `created_at`. */\nexport function earlierEvent(a: Nostr.Event, b: Nostr.Event): Nostr.Event {\n return compareEvents(a, b) < 0 ? a : b;\n}\n\n/** Return an event that has later `created_at`. */\nexport function laterEvent(a: Nostr.Event, b: Nostr.Event): Nostr.Event {\n return compareEvents(a, b) < 0 ? b : a;\n}\n\n/** Sort key function to sort events based on `created_at`. */\nexport function compareEvents(a: Nostr.Event, b: Nostr.Event): number {\n if (a.id === b.id) {\n return 0;\n }\n\n return a.created_at < b.created_at ||\n // https://github.com/nostr-protocol/nips/blob/master/16.md#replaceable-events\n (a.created_at === b.created_at && a.id < b.id)\n ? -1\n : 1;\n}\n","export function inlineThrow(err: Error): never {\n throw err;\n}\n","import * as Nostr from \"nostr-typedef\";\n\nimport { RxNostrEnvironmentError, RxNostrInvalidUsageError } from \"../error.js\";\nimport { ensureEventFields } from \"../nostr/event.js\";\nimport { inlineThrow } from \"../utils/inline-throw.js\";\n\nexport interface EventSigner {\n signEvent<K extends number>(\n params: Nostr.EventParameters<K>,\n ): Promise<Nostr.Event<K>>;\n getPublicKey(): Promise<string>;\n}\n\nexport interface EventSignerOptions {\n /** If set, the set tags is appended to the end of the given event's tags on signing. */\n tags?: Nostr.Tag.Any[];\n}\n\nexport function nip07Signer(options?: EventSignerOptions): EventSigner {\n return {\n async signEvent<K extends number>(\n params: Nostr.EventParameters<K>,\n ): Promise<Nostr.Event<K>> {\n const event = {\n ...params,\n pubkey:\n params.pubkey ??\n (await window?.nostr?.getPublicKey()) ??\n inlineThrow(\n new RxNostrEnvironmentError(\n \"window.nostr.getPublicKey() is not found\",\n ),\n ),\n tags: [...(params.tags ?? []), ...(options?.tags ?? [])],\n created_at: params.created_at ?? Math.floor(Date.now() / 1000),\n };\n\n if (ensureEventFields(event)) {\n return event;\n }\n\n return (\n (await window?.nostr?.signEvent(event)) ??\n inlineThrow(\n new RxNostrEnvironmentError(\"window.nostr.signEvent() is not found\"),\n )\n );\n },\n getPublicKey() {\n return (\n window?.nostr?.getPublicKey() ??\n inlineThrow(\n new RxNostrEnvironmentError(\n \"window.nostr.getPublicKey() is not found\",\n ),\n )\n );\n },\n };\n}\n\nexport function noopSigner(): EventSigner {\n return {\n async signEvent<K extends number>(params: Nostr.EventParameters<K>) {\n return params as Nostr.Event<K>;\n },\n async getPublicKey() {\n throw new RxNostrInvalidUsageError(\"noopSigner cannot calculate pubkey.\");\n },\n };\n}\n","import * as Nostr from \"nostr-typedef\";\n\nexport interface EventVerifier {\n (params: Nostr.Event): Promise<boolean>;\n}\n\nexport const noopVerifier: EventVerifier = async () => true;\n\nexport const emptyVerifier: EventVerifier = async () => {\n throw new Error(\n \"You must give some verifier to createRxNostr(). In most cases, @rx-nostr/crypto packages will help you.\",\n );\n};\n","import { fill } from \"../utils/config.js\";\nimport type { IWebSocketConstructor } from \"../websocket.js\";\nimport { AuthenticatorConfig } from \"./authenticator.js\";\nimport { EventSigner, nip07Signer } from \"./signer.js\";\nimport { emptyVerifier, EventVerifier } from \"./verifier.js\";\n\nexport const makeRxNostrConfig = (config: RxNostrConfig) =>\n fill(config, {\n signer: nip07Signer(),\n verifier: emptyVerifier,\n connectionStrategy: \"lazy\",\n retry: {\n strategy: \"exponential\",\n maxCount: 5,\n initialDelay: 1000,\n },\n disconnectTimeout: 10000,\n eoseTimeout: 30 * 1000,\n okTimeout: 30 * 1000,\n authTimeout: 30 * 1000,\n skipVerify: false,\n skipValidateFilterMatching: false,\n skipExpirationCheck: false,\n skipFetchNip11: false,\n });\nexport type FilledRxNostrConfig = ReturnType<typeof makeRxNostrConfig>;\n\n/**\n * Configuration object for a RxNostr instance.\n */\nexport interface RxNostrConfig {\n /**\n * Default signer, which is used to convert event parameters into signed event.\n */\n signer?: EventSigner;\n /**\n * Default verifier, which is used to verify event's signature.\n */\n verifier?: EventVerifier;\n authenticator?:\n | AuthenticatorConfig\n | ((relay: string) => AuthenticatorConfig);\n\n /**\n * Connection strategy for default relays.\n */\n connectionStrategy?: ConnectionStrategy;\n\n /**\n * Auto reconnection strategy.\n */\n retry?: RetryConfig;\n\n /**\n * How long temporary relay connections should be held open when no longer used.\n * Applies to default relays when connectionStrategy is set to \"lazy\"\n * @default 10000\n */\n disconnectTimeout?: number;\n\n /**\n * Specify how long rx-nostr waits for EOSE messages in `use()` following backward strategy (milliseconds).\n *\n * If EOSE doesn't come after waiting for this amount of time,\n * rx-nostr is considered to get EOSE.\n */\n eoseTimeout?: number;\n /**\n * Specify how long rx-nostr waits for OK messages in `send()` (milliseconds).\n *\n * If OK doesn't come after waiting for this amount of time,\n * rx-nostr stops listening OK and the Observable come from `send()` finishes with TimeoutError.\n */\n okTimeout?: number;\n authTimeout?: number;\n /**\n * If true, skip filtering EVENTs based on signature verification.\n */\n skipVerify?: boolean;\n /**\n * If true, skip filtering EVENTs based on matching with REQ filter.\n */\n skipValidateFilterMatching?: boolean;\n /**\n * If true, skip automatic expiration check based on NIP-40.\n */\n skipExpirationCheck?: boolean;\n /**\n * If true, skip automatic fetching NIP-11 relay information.\n */\n skipFetchNip11?: boolean;\n /**\n * Optional. For environments where `WebSocket` doesn't exist in `globalThis` such as Node.js.\n */\n websocketCtor?: IWebSocketConstructor;\n}\n\n/**\n * Auto reconnection strategy.\n *\n * `strategy` can be one of the followings:\n *\n * - `\"exponential\"`: Exponential backoff and jitter strategy.\n * - `\"linear\"`: Retry at regular intervals.\n * - `\"immediately\"`: Retry immediately.\n * - `\"off\"`: Won't retry.\n *\n * Options:\n *\n * - `maxCount` specifies the maximum number of consecutive retry attempts.\n * - `polite` specifies whether to retry only if the relay is alive.\n * If true, rx-nostr doesn't try to retry against a relay doesn't respond to the first request.\n */\nexport type RetryConfig =\n | {\n strategy: \"exponential\";\n maxCount: number;\n initialDelay: number;\n polite?: boolean;\n }\n | {\n strategy: \"linear\";\n maxCount: number;\n interval: number;\n polite?: boolean;\n }\n | {\n strategy: \"immediately\";\n maxCount: number;\n polite?: boolean;\n }\n | {\n strategy: \"off\";\n };\n\n/**\n * Connection strategy for default relays.\n *\n * - `\"lazy\"`: Connect when needed, and disconnect when unneeded.\n * - `\"lazy-keep\"`: Connect when needed, when the relay gets to be non-default and it is unneeded.\n * - `\"aggressive\"`: Connect immediately, and disconnect when the relay gets to be non-default and it is unneeded.\n */\nexport type ConnectionStrategy = \"lazy\" | \"lazy-keep\" | \"aggressive\";\n","import * as Nostr from \"nostr-typedef\";\n\nimport { LazyFilter } from \"./packet.js\";\n\n/**\n * Evaluate one or more `LazyFilter`s and return `Nostr.Filter[]`.\n */\nexport function evalFilters(\n filters: LazyFilter | LazyFilter[],\n): Nostr.Filter[] {\n if (\"length\" in filters) {\n return filters.map(evalFilter);\n } else {\n return [evalFilter(filters)];\n }\n}\n\nfunction evalFilter(filter: LazyFilter): Nostr.Filter {\n return {\n ...filter,\n since: filter.since ? evalLazyNumber(filter.since) : undefined,\n until: filter.until ? evalLazyNumber(filter.until) : undefined,\n };\n}\n\nfunction evalLazyNumber(lazyNumber: number | (() => number)): number {\n return typeof lazyNumber === \"number\" ? lazyNumber : lazyNumber();\n}\n","import * as Nostr from \"nostr-typedef\";\n\n/**\n * Fetch relay's information based on [NIP-11](https://github.com/nostr-protocol/nips/blob/master/11.md).\n */\nexport async function fetchRelayInfo(\n url: string,\n): Promise<Nostr.Nip11.RelayInfo> {\n try {\n const u = new URL(url);\n u.protocol = u.protocol.replace(/^ws(s?):/, \"http$1:\");\n\n const res = await fetch(u.toString(), {\n headers: { Accept: \"application/nostr+json\" },\n });\n return await res.json();\n } catch {\n return {};\n }\n}\n","export function inlineTry<T, U>(\n f: () => T,\n g: U | ((err: unknown) => U),\n): T | U {\n try {\n return f();\n } catch (err) {\n if (g instanceof Function) {\n return g(err);\n } else {\n return g;\n }\n }\n}\n","import { inlineTry } from \"./inline-try.js\";\n\nexport function normalizeRelayUrl(url: string) {\n let o = \"\";\n\n try {\n o = url.trim();\n\n const u = new URL(o);\n\n u.hash = \"\";\n u.pathname = inlineTry(() => decodeURI(u.pathname), u.pathname);\n u.pathname = u.pathname.replace(/\\/$/, \"\");\n u.hostname = u.hostname.replace(/\\.$/, \"\");\n u.searchParams.sort();\n u.search = inlineTry(() => decodeURIComponent(u.search), u.search);\n\n let s = u.toString();\n if (!u.search) {\n s = s.replace(/\\/$/, \"\");\n }\n\n return s;\n } catch {\n return o;\n }\n}\n","import { normalizeRelayUrl } from \"./normalize-url.js\";\n\nexport class UrlMap<T> extends Map<string, T> {\n constructor(obj?: Record<string, T>) {\n super();\n\n if (!obj) {\n return;\n }\n\n for (const [url, v] of Object.entries(obj)) {\n this.set(normalizeRelayUrl(url), v);\n }\n }\n get(url: string) {\n return super.get(normalizeRelayUrl(url));\n }\n getMany(urls: string[]) {\n const vs: T[] = [];\n\n for (const url of new Set(urls.map(normalizeRelayUrl))) {\n const v = this.get(url);\n if (v !== undefined) {\n vs.push(v);\n }\n }\n\n return vs;\n }\n set(url: string, v: T) {\n return super.set(normalizeRelayUrl(url), v);\n }\n has(url: string): boolean {\n return super.has(normalizeRelayUrl(url));\n }\n delete(url: string) {\n return super.delete(normalizeRelayUrl(url));\n }\n toObject(): Record<string, T> {\n const obj: Record<string, T> = {};\n\n for (const [url, v] of this.entries()) {\n obj[url] = v;\n }\n\n return obj;\n }\n toKeys(): string[] {\n return [...super.keys()];\n }\n toValues(): T[] {\n return [...super.values()];\n }\n copy() {\n return new UrlMap(this.toObject());\n }\n}\n","import * as Nostr from \"nostr-typedef\";\n\nimport { fetchRelayInfo } from \"./nostr/nip11.js\";\nimport { UrlMap } from \"./utils/url-map.js\";\n\n/**\n * This is used by rx-nostr to access NIP-11 relay information.\n * rx-nostr works adaptively to the [`limitation`](https://github.com/nostr-protocol/nips/blob/master/11.md#server-limitations) defined by NIP-11.\n *\n * If you `set()` or `setDefault()` NIP-11 relay information in advance,\n * rx-nostr will use them instead of fetching even if `skipFetchNip11` is enabled.\n */\nexport class Nip11Registry {\n private static cache = new UrlMap<\n Promise<Nostr.Nip11.RelayInfo> | Nostr.Nip11.RelayInfo\n >();\n private static default: Nostr.Nip11.RelayInfo = {};\n\n static async getValue<T>(\n url: string,\n getter: (data: Nostr.Nip11.RelayInfo) => T,\n options?: {\n skipFetch?: boolean;\n skipCache?: boolean;\n },\n ): Promise<T> {\n if (!options?.skipCache) {\n const data = await this.cache.get(url);\n if (data) {\n return getter(data);\n }\n }\n if (!options?.skipFetch) {\n const data = await this.fetch(url);\n if (data) {\n return getter(data);\n }\n }\n\n return getter(this.default);\n }\n\n /**\n * Return cached or `set()`'ed NIP-11 information.\n */\n static get(url: string): Nostr.Nip11.RelayInfo | undefined {\n const v = this.cache.get(url);\n if (v && !(v instanceof Promise)) {\n return v;\n } else {\n return undefined;\n }\n }\n\n /**\n * Cache fetched information then return it.\n */\n static async fetch(url: string) {\n const promise = fetchRelayInfo(url);\n\n this.cache.set(url, promise);\n promise.then((v) => {\n this.cache.set(url, v);\n });\n\n return promise;\n }\n\n /**\n * Return cached or `set()`'ed NIP-11 information,\n * or cache fetched information then return it.\n */\n static async getOrFetch(url: string): Promise<Nostr.Nip11.RelayInfo> {\n return this.cache.get(url) ?? this.fetch(url);\n }\n\n /**\n * Set NIP-11 information manually for given relay URL.\n */\n static set(url: string, nip11: Nostr.Nip11.RelayInfo) {\n this.cache.set(url, nip11);\n }\n\n /**\n * Get NIP-11 information for fallback.\n */\n static getDefault(): Nostr.Nip11.RelayInfo {\n return this.default;\n }\n\n /**\n * Set NIP-11 information for fallback.\n */\n static setDefault(nip11: Nostr.Nip11.RelayInfo) {\n this.default = nip11;\n }\n\n /**\n * Forget cached NIP-11 information for given relay URL.\n */\n static forget(url: string) {\n this.cache.delete(url);\n }\n\n /**\n * Forget all cached NIP-11 information.\n *\n * This doesn't erase `setDefault()`'ed value.\n * If you want it, you can `setDefault({})` instead.\n */\n static forgetAll() {\n this.cache.clear();\n }\n}\n","/**\n * Checks if something is Uint8Array. Be careful: nodejs Buffer will return true.\n * @param a - value to test\n * @returns `true` when the value is a Uint8Array-compatible view.\n * @example\n * Check whether a value is a Uint8Array-compatible view.\n * ```ts\n * isBytes(new Uint8Array([1, 2, 3]));\n * ```\n */\nexport function isBytes(a) {\n // Plain `instanceof Uint8Array` is too strict for some Buffer / proxy / cross-realm cases.\n // The fallback still requires a real ArrayBuffer view, so plain\n // JSON-deserialized `{ constructor: ... }` spoofing is rejected, and\n // `BYTES_PER_ELEMENT === 1` keeps the fallback on byte-oriented views.\n return (a instanceof Uint8Array ||\n (ArrayBuffer.isView(a) &&\n a.constructor.name === 'Uint8Array' &&\n 'BYTES_PER_ELEMENT' in a &&\n a.BYTES_PER_ELEMENT === 1));\n}\n/**\n * Asserts something is a non-negative integer.\n * @param n - number to validate\n * @param title - label included in thrown errors\n * @throws On wrong argument types. {@link TypeError}\n * @throws On wrong argument ranges or values. {@link RangeError}\n * @example\n * Validate a non-negative integer option.\n * ```ts\n * anumber(32, 'length');\n * ```\n */\nexport function anumber(n, title = '') {\n if (typeof n !== 'number') {\n const prefix = title && `\"${title}\" `;\n throw new TypeError(`${prefix}expected number, got ${typeof n}`);\n }\n if (!Number.isSafeInteger(n) || n < 0) {\n const prefix = title && `\"${title}\" `;\n throw new RangeError(`${prefix}expected integer >= 0, got ${n}`);\n }\n}\n/**\n * Asserts something is Uint8Array.\n * @param value - value to validate\n * @param length - optional exact length constraint\n * @param title - label included in thrown errors\n * @returns The validated byte array.\n * @throws On wrong argument types. {@link TypeError}\n * @throws On wrong argument ranges or values. {@link RangeError}\n * @example\n * Validate that a value is a byte array.\n * ```ts\n * abytes(new Uint8Array([1, 2, 3]));\n * ```\n */\nexport function abytes(value, length, title = '') {\n const bytes = isBytes(value);\n const len = value?.length;\n const needsLen = length !== undefined;\n if (!bytes || (needsLen && len !== length)) {\n const prefix = title && `\"${title}\" `;\n const ofLen = needsLen ? ` of length ${length}` : '';\n const got = bytes ? `length=${len}` : `type=${typeof value}`;\n const message = prefix + 'expected Uint8Array' + ofLen + ', got ' + got;\n if (!bytes)\n throw new TypeError(message);\n throw new RangeError(message);\n }\n return value;\n}\n/**\n * Copies bytes into a fresh Uint8Array.\n * Buffer-style slices can alias the same backing store, so callers that need ownership should copy.\n * @param bytes - source bytes to clone\n * @returns Freshly allocated copy of `bytes`.\n * @throws On wrong argument types. {@link TypeError}\n * @example\n * Clone a byte array before mutating it.\n * ```ts\n * const copy = copyBytes(new Uint8Array([1, 2, 3]));\n * ```\n */\nexport function copyBytes(bytes) {\n // `Uint8Array.from(...)` would also accept arrays / other typed arrays. Keep this helper strict\n // because callers use it at byte-validation boundaries before mutating the detached copy.\n return Uint8Array.from(abytes(bytes));\n}\n/**\n * Asserts something is a wrapped hash constructor.\n * @param h - hash constructor to validate\n * @throws On wrong argument types or invalid hash wrapper shape. {@link TypeError}\n * @throws On invalid hash metadata ranges or values. {@link RangeError}\n * @throws If the hash metadata allows empty outputs or block sizes. {@link Error}\n * @example\n * Validate a callable hash wrapper.\n * ```ts\n * import { ahash } from '@noble/hashes/utils.js';\n * import { sha256 } from '@noble/hashes/sha2.js';\n * ahash(sha256);\n * ```\n */\nexport function ahash(h) {\n if (typeof h !== 'function' || typeof h.create !== 'function')\n throw new TypeError('Hash must wrapped by utils.createHasher');\n anumber(h.outputLen);\n anumber(h.blockLen);\n // HMAC and KDF callers treat these as real byte lengths; allowing zero lets fake wrappers pass\n // validation and can produce empty outputs instead of failing fast.\n if (h.outputLen < 1)\n throw new Error('\"outputLen\" must be >= 1');\n if (h.blockLen < 1)\n throw new Error('\"blockLen\" must be >= 1');\n}\n/**\n * Asserts a hash instance has not been destroyed or finished.\n * @param instance - hash instance to validate\n * @param checkFinished - whether to reject finalized instances\n * @throws If the hash instance has already been destroyed or finalized. {@link Error}\n * @example\n * Validate that a hash instance is still usable.\n * ```ts\n * import { aexists } from '@noble/hashes/utils.js';\n * import { sha256 } from '@noble/hashes/sha2.js';\n * const hash = sha256.create();\n * aexists(hash);\n * ```\n */\nexport function aexists(instance, checkFinished = true) {\n if (instance.destroyed)\n throw new Error('Hash instance has been destroyed');\n if (checkFinished && instance.finished)\n throw new Error('Hash#digest() has already been called');\n}\n/**\n * Asserts output is a sufficiently-sized byte array.\n * @param out - destination buffer\n * @param instance - hash instance providing output length\n * Oversized buffers are allowed; downstream code only promises to fill the first `outputLen` bytes.\n * @throws On wrong argument types. {@link TypeError}\n * @throws On wrong argument ranges or values. {@link RangeError}\n * @example\n * Validate a caller-provided digest buffer.\n * ```ts\n * import { aoutput } from '@noble/hashes/utils.js';\n * import { sha256 } from '@noble/hashes/sha2.js';\n * const hash = sha256.create();\n * aoutput(new Uint8Array(hash.outputLen), hash);\n * ```\n */\nexport function aoutput(out, instance) {\n abytes(out, undefined, 'digestInto() output');\n const min = instance.outputLen;\n if (out.length < min) {\n throw new RangeError('\"digestInto() output\" expected to be of length >=' + min);\n }\n}\n/**\n * Casts a typed array view to Uint8Array.\n * @param arr - source typed array\n * @returns Uint8Array view over the same buffer.\n * @example\n * Reinterpret a typed array as bytes.\n * ```ts\n * u8(new Uint32Array([1, 2]));\n * ```\n */\nexport function u8(arr) {\n return new Uint8Array(arr.buffer, arr.byteOffset, arr.byteLength);\n}\n/**\n * Casts a typed array view to Uint32Array.\n * `arr.byteOffset` must already be 4-byte aligned or the platform\n * Uint32Array constructor will throw.\n * @param arr - source typed array\n * @returns Uint32Array view over the same buffer.\n * @example\n * Reinterpret a byte array as 32-bit words.\n * ```ts\n * u32(new Uint8Array(8));\n * ```\n */\nexport function u32(arr) {\n return new Uint32Array(arr.buffer, arr.byteOffset, Math.floor(arr.byteLength / 4));\n}\n/**\n * Zeroizes typed arrays in place. Warning: JS provides no guarantees.\n * @param arrays - arrays to overwrite with zeros\n * @example\n * Zeroize sensitive buffers in place.\n * ```ts\n * clean(new Uint8Array([1, 2, 3]));\n * ```\n */\nexport function clean(...arrays) {\n for (let i = 0; i < arrays.length; i++) {\n arrays[i].fill(0);\n }\n}\n/**\n * Creates a DataView for byte-level manipulation.\n * @param arr - source typed array\n * @returns DataView over the same buffer region.\n * @example\n * Create a DataView over an existing buffer.\n * ```ts\n * createView(new Uint8Array(4));\n * ```\n */\nexport function createView(arr) {\n return new DataView(arr.buffer, arr.byteOffset, arr.byteLength);\n}\n/**\n * Rotate-right operation for uint32 values.\n * @param word - source word\n * @param shift - shift amount in bits\n * @returns Rotated word.\n * @example\n * Rotate a 32-bit word to the right.\n * ```ts\n * rotr(0x12345678, 8);\n * ```\n */\nexport function rotr(word, shift) {\n return (word << (32 - shift)) | (word >>> shift);\n}\n/**\n * Rotate-left operation for uint32 values.\n * @param word - source word\n * @param shift - shift amount in bits\n * @returns Rotated word.\n * @example\n * Rotate a 32-bit word to the left.\n * ```ts\n * rotl(0x12345678, 8);\n * ```\n */\nexport function rotl(word, shift) {\n return (word << shift) | ((word >>> (32 - shift)) >>> 0);\n}\n/** Whether the current platform is little-endian. */\nexport const isLE = /* @__PURE__ */ (() => new Uint8Array(new Uint32Array([0x11223344]).buffer)[0] === 0x44)();\n/**\n * Byte-swap operation for uint32 values.\n * @param word - source word\n * @returns Word with reversed byte order.\n * @example\n * Reverse the byte order of a 32-bit word.\n * ```ts\n * byteSwap(0x11223344);\n * ```\n */\nexport function byteSwap(word) {\n return (((word << 24) & 0xff000000) |\n ((word << 8) & 0xff0000) |\n ((word >>> 8) & 0xff00) |\n ((word >>> 24) & 0xff));\n}\n/**\n * Conditionally byte-swaps one 32-bit word on big-endian platforms.\n * @param n - source word\n * @returns Original or byte-swapped word depending on platform endianness.\n * @example\n * Normalize a 32-bit word for host endianness.\n * ```ts\n * swap8IfBE(0x11223344);\n * ```\n */\nexport const swap8IfBE = isLE\n ? (n) => n\n : (n) => byteSwap(n) >>> 0;\n/**\n * Byte-swaps every word of a Uint32Array in place.\n * @param arr - array to mutate\n * @returns The same array after mutation; callers pass live state arrays here.\n * @example\n * Reverse the byte order of every word in place.\n * ```ts\n * byteSwap32(new Uint32Array([0x11223344]));\n * ```\n */\nexport function byteSwap32(arr) {\n for (let i = 0; i < arr.length; i++) {\n arr[i] = byteSwap(arr[i]);\n }\n return arr;\n}\n/**\n * Conditionally byte-swaps a Uint32Array on big-endian platforms.\n * @param u - array to normalize for host endianness\n * @returns Original or byte-swapped array depending on platform endianness.\n * On big-endian runtimes this mutates `u` in place via `byteSwap32(...)`.\n * @example\n * Normalize a word array for host endianness.\n * ```ts\n * swap32IfBE(new Uint32Array([0x11223344]));\n * ```\n */\nexport const swap32IfBE = isLE\n ? (u) => u\n : byteSwap32;\n// Built-in hex conversion https://caniuse.com/mdn-javascript_builtins_uint8array_fromhex\nconst hasHexBuiltin = /* @__PURE__ */ (() => \n// @ts-ignore\ntypeof Uint8Array.from([]).toHex === 'function' && typeof Uint8Array.fromHex === 'function')();\n// Array where index 0xf0 (240) is mapped to string 'f0'\nconst hexes = /* @__PURE__ */ Array.from({ length: 256 }, (_, i) => i.toString(16).padStart(2, '0'));\n/**\n * Convert byte array to hex string.\n * Uses the built-in function when available and assumes it matches the tested\n * fallback semantics.\n * @param bytes - bytes to encode\n * @returns Lowercase hexadecimal string.\n * @throws On wrong argument types. {@link TypeError}\n * @example\n * Convert bytes to lowercase hexadecimal.\n * ```ts\n * bytesToHex(Uint8Array.from([0xca, 0xfe, 0x01, 0x23])); // 'cafe0123'\n * ```\n */\nexport function bytesToHex(bytes) {\n abytes(bytes);\n // @ts-ignore\n if (hasHexBuiltin)\n return bytes.toHex();\n // pre-caching improves the speed 6x\n let hex = '';\n for (let i = 0; i < bytes.length; i++) {\n hex += hexes[bytes[i]];\n }\n return hex;\n}\n// We use optimized technique to convert hex string to byte array\nconst asciis = { _0: 48, _9: 57, A: 65, F: 70, a: 97, f: 102 };\nfunction asciiToBase16(ch) {\n if (ch >= asciis._0 && ch <= asciis._9)\n return ch - asciis._0; // '2' => 50-48\n if (ch >= asciis.A && ch <= asciis.F)\n return ch - (asciis.A - 10); // 'B' => 66-(65-10)\n if (ch >= asciis.a && ch <= asciis.f)\n return ch - (asciis.a - 10); // 'b' => 98-(97-10)\n return;\n}\n/**\n * Convert hex string to byte array. Uses built-in function, when available.\n * @param hex - hexadecimal string to decode\n * @returns Decoded bytes.\n * @throws On wrong argument types. {@link TypeError}\n * @throws On wrong argument ranges or values. {@link RangeError}\n * @example\n * Decode lowercase hexadecimal into bytes.\n * ```ts\n * hexToBytes('cafe0123'); // Uint8Array.from([0xca, 0xfe, 0x01, 0x23])\n * ```\n */\nexport function hexToBytes(hex) {\n if (typeof hex !== 'string')\n throw new TypeError('hex string expected, got ' + typeof hex);\n if (hasHexBuiltin) {\n try {\n return Uint8Array.fromHex(hex);\n }\n catch (error) {\n if (error instanceof SyntaxError)\n throw new RangeError(error.message);\n throw error;\n }\n }\n const hl = hex.length;\n const al = hl / 2;\n if (hl % 2)\n throw new RangeError('hex string expected, got unpadded hex of length ' + hl);\n const array = new Uint8Array(al);\n for (let ai = 0, hi = 0; ai < al; ai++, hi += 2) {\n const n1 = asciiToBase16(hex.charCodeAt(hi));\n const n2 = asciiToBase16(hex.charCodeAt(hi + 1));\n if (n1 === undefined || n2 === undefined) {\n const char = hex[hi] + hex[hi + 1];\n throw new RangeError('hex string expected, got non-hex character \"' + char + '\" at index ' + hi);\n }\n array[ai] = n1 * 16 + n2; // multiply first octet, e.g. 'a3' => 10*16+3 => 160 + 3 => 163\n }\n return array;\n}\n/**\n * There is no setImmediate in browser and setTimeout is slow.\n * This yields to the Promise/microtask scheduler queue, not to timers or the\n * full macrotask event loop.\n * @example\n * Yield to the next scheduler tick.\n * ```ts\n * await nextTick();\n * ```\n */\nexport const nextTick = async () => { };\n/**\n * Returns control to the Promise/microtask scheduler every `tick`\n * milliseconds to avoid blocking long loops.\n * @param iters - number of loop iterations to run\n * @param tick - maximum time slice in milliseconds\n * @param cb - callback executed on each iteration\n * @example\n * Run a loop that periodically yields back to the event loop.\n * ```ts\n * await asyncLoop(2, 0, () => {});\n * ```\n */\nexport async function asyncLoop(iters, tick, cb) {\n let ts = Date.now();\n for (let i = 0; i < iters; i++) {\n cb(i);\n // Date.now() is not monotonic, so in case if clock goes backwards we return return control too\n const diff = Date.now() - ts;\n if (diff >= 0 && diff < tick)\n continue;\n await nextTick();\n ts += diff;\n }\n}\n/**\n * Converts string to bytes using UTF8 encoding.\n * Built-in doesn't validate input to be string: we do the check.\n * Non-ASCII details are delegated to the platform `TextEncoder`.\n * @param str - string to encode\n * @returns UTF-8 encoded bytes.\n * @throws On wrong argument types. {@link TypeError}\n * @example\n * Encode a string as UTF-8 bytes.\n * ```ts\n * utf8ToBytes('abc'); // Uint8Array.from([97, 98, 99])\n * ```\n */\nexport function utf8ToBytes(str) {\n if (typeof str !== 'string')\n throw new TypeError('string expected');\n return new Uint8Array(new TextEncoder().encode(str)); // https://bugzil.la/1681809\n}\n/**\n * Helper for KDFs: consumes Uint8Array or string.\n * String inputs are UTF-8 encoded; byte-array inputs stay aliased to the caller buffer.\n * @param data - user-provided KDF input\n * @param errorTitle - label included in thrown errors\n * @returns Byte representation of the input.\n * @throws On wrong argument types. {@link TypeError}\n * @example\n * Normalize KDF input to bytes.\n * ```ts\n * kdfInputToBytes('password');\n * ```\n */\nexport function kdfInputToBytes(data, errorTitle = '') {\n if (typeof data === 'string')\n return utf8ToBytes(data);\n return abytes(data, undefined, errorTitle);\n}\n/**\n * Copies several Uint8Arrays into one.\n * @param arrays - arrays to concatenate\n * @returns Concatenated byte array.\n * @throws On wrong argument types. {@link TypeError}\n * @example\n * Concatenate multiple byte arrays.\n * ```ts\n * concatBytes(new Uint8Array([1]), new Uint8Array([2]));\n * ```\n */\nexport function concatBytes(...arrays) {\n let sum = 0;\n for (let i = 0; i < arrays.length; i++) {\n const a = arrays[i];\n abytes(a);\n sum += a.length;\n }\n const res = new Uint8Array(sum);\n for (let i = 0, pad = 0; i < arrays.length; i++) {\n const a = arrays[i];\n res.set(a, pad);\n pad += a.length;\n }\n return res;\n}\n/**\n * Merges default options and passed options.\n * @param defaults - base option object\n * @param opts - user overrides\n * @returns Merged option object. The merge mutates `defaults` in place.\n * @throws On wrong argument types. {@link TypeError}\n * @example\n * Merge user overrides onto default options.\n * ```ts\n * checkOpts({ dkLen: 32 }, { asyncTick: 10 });\n * ```\n */\nexport function checkOpts(defaults, opts) {\n if (opts !== undefined && {}.toString.call(opts) !== '[object Object]')\n throw new TypeError('options must be object or undefined');\n const merged = Object.assign(defaults, opts);\n return merged;\n}\n/**\n * Creates a callable hash function from a stateful class constructor.\n * @param hashCons - hash constructor or factory\n * @param info - optional metadata such as DER OID\n * @returns Frozen callable hash wrapper with `.create()`.\n * Wrapper construction eagerly calls `hashCons(undefined)` once to read\n * `outputLen` / `blockLen`, so constructor side effects happen at module\n * init time.\n * @example\n * Wrap a stateful hash constructor into a callable helper.\n * ```ts\n * import { createHasher } from '@noble/hashes/utils.js';\n * import { sha256 } from '@noble/hashes/sha2.js';\n * const wrapped = createHasher(sha256.create, { oid: sha256.oid });\n * wrapped(new Uint8Array([1]));\n * ```\n */\nexport function createHasher(hashCons, info = {}) {\n const hashC = (msg, opts) => hashCons(opts)\n .update(msg)\n .digest();\n const tmp = hashCons(undefined);\n hashC.outputLen = tmp.outputLen;\n hashC.blockLen = tmp.blockLen;\n hashC.canXOF = tmp.canXOF;\n hashC.create = (opts) => hashCons(opts);\n Object.assign(hashC, info);\n return Object.freeze(hashC);\n}\n/**\n * Cryptographically secure PRNG backed by `crypto.getRandomValues`.\n * @param bytesLength - number of random bytes to generate\n * @returns Random bytes.\n * The platform `getRandomValues()` implementation still defines any\n * single-call length cap, and this helper rejects oversize requests\n * with a stable library `RangeError` instead of host-specific errors.\n * @throws On wrong argument types. {@link TypeError}\n * @throws On wrong argument ranges or values. {@link RangeError}\n * @throws If the current runtime does not provide `crypto.getRandomValues`. {@link Error}\n * @example\n * Generate a fresh random key or nonce.\n * ```ts\n * const key = randomBytes(16);\n * ```\n */\nexport function randomBytes(bytesLength = 32) {\n // Match the repo's other length-taking helpers instead of relying on Uint8Array coercion.\n anumber(bytesLength, 'bytesLength');\n const cr = typeof globalThis === 'object' ? globalThis.crypto : null;\n if (typeof cr?.getRandomValues !== 'function')\n throw new Error('crypto.getRandomValues must be defined');\n // Web Cryptography API Level 2 §10.1.1:\n // if `byteLength > 65536`, throw `QuotaExceededError`.\n // Keep the guard explicit so callers can see the quota in code\n // instead of discovering it by reading the spec or host errors.\n // This wrapper surfaces the same quota as a stable library RangeError.\n if (bytesLength > 65536)\n throw new RangeError(`\"bytesLength\" expected <= 65536, got ${bytesLength}`);\n return cr.getRandomValues(new Uint8Array(bytesLength));\n}\n/**\n * Creates OID metadata for NIST hashes with prefix `06 09 60 86 48 01 65 03 04 02`.\n * @param suffix - final OID byte for the selected hash.\n * The helper accepts any byte even though only the documented NIST hash\n * suffixes are meaningful downstream.\n * @returns Object containing the DER-encoded OID.\n * @example\n * Build OID metadata for a NIST hash.\n * ```ts\n * oidNist(0x01);\n * ```\n */\nexport const oidNist = (suffix) => ({\n // Current NIST hashAlgs suffixes used here fit in one DER subidentifier octet.\n // Larger suffix values would need base-128 OID encoding and a different length byte.\n oid: Uint8Array.from([0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, suffix]),\n});\n//# sourceMappingURL=utils.js.map","/*! scure-base - MIT License (c) 2022 Paul Miller (paulmillr.com) */\nfunction isBytes(a) {\n return a instanceof Uint8Array || (ArrayBuffer.isView(a) && a.constructor.name === 'Uint8Array');\n}\n/** Asserts something is Uint8Array. */\nfunction abytes(b) {\n if (!isBytes(b))\n throw new Error('Uint8Array expected');\n}\nfunction isArrayOf(isString, arr) {\n if (!Array.isArray(arr))\n return false;\n if (arr.length === 0)\n return true;\n if (isString) {\n return arr.every((item) => typeof item === 'string');\n }\n else {\n return arr.every((item) => Number.isSafeInteger(item));\n }\n}\nfunction afn(input) {\n if (typeof input !== 'function')\n throw new Error('function expected');\n return true;\n}\nfunction astr(label, input) {\n if (typeof input !== 'string')\n throw new Error(`${label}: string expected`);\n return true;\n}\nfunction anumber(n) {\n if (!Number.isSafeInteger(n))\n throw new Error(`invalid integer: ${n}`);\n}\nfunction aArr(input) {\n if (!Array.isArray(input))\n throw new Error('array expected');\n}\nfunction astrArr(label, input) {\n if (!isArrayOf(true, input))\n throw new Error(`${label}: array of strings expected`);\n}\nfunction anumArr(label, input) {\n if (!isArrayOf(false, input))\n throw new Error(`${label}: array of numbers expected`);\n}\n/**\n * @__NO_SIDE_EFFECTS__\n */\nfunction chain(...args) {\n const id = (a) => a;\n // Wrap call in closure so JIT can inline calls\n const wrap = (a, b) => (c) => a(b(c));\n // Construct chain of args[-1].encode(args[-2].encode([...]))\n const encode = args.map((x) => x.encode).reduceRight(wrap, id);\n // Construct chain of args[0].decode(args[1].decode(...))\n const decode = args.map((x) => x.decode).reduce(wrap, id);\n return { encode, decode };\n}\n/**\n * Encodes integer radix representation to array of strings using alphabet and back.\n * Could also be array of strings.\n * @__NO_SIDE_EFFECTS__\n */\nfunction alphabet(letters) {\n // mapping 1 to \"b\"\n const lettersA = typeof letters === 'string' ? letters.split('') : letters;\n const len = lettersA.length;\n astrArr('alphabet', lettersA);\n // mapping \"b\" to 1\n const indexes = new Map(lettersA.map((l, i) => [l, i]));\n return {\n encode: (digits) => {\n aArr(digits);\n return digits.map((i) => {\n if (!Number.isSafeInteger(i) || i < 0 || i >= len)\n throw new Error(`alphabet.encode: digit index outside alphabet \"${i}\". Allowed: ${letters}`);\n return lettersA[i];\n });\n },\n decode: (input) => {\n aArr(input);\n return input.map((letter) => {\n astr('alphabet.decode', letter);\n const i = indexes.get(letter);\n if (i === undefined)\n throw new Error(`Unknown letter: \"${letter}\". Allowed: ${letters}`);\n return i;\n });\n },\n };\n}\n/**\n * @__NO_SIDE_EFFECTS__\n */\nfunction join(separator = '') {\n astr('join', separator);\n return {\n encode: (from) => {\n astrArr('join.decode', from);\n return from.join(separator);\n },\n decode: (to) => {\n astr('join.decode', to);\n return to.split(separator);\n },\n };\n}\n/**\n * Pad strings array so it has integer number of bits\n * @__NO_SIDE_EFFECTS__\n */\nfunction padding(bits, chr = '=') {\n anumber(bits);\n astr('padding', chr);\n return {\n encode(data) {\n astrArr('padding.encode', data);\n while ((data.length * bits) % 8)\n data.push(chr);\n return data;\n },\n decode(input) {\n astrArr('padding.decode', input);\n let end = input.length;\n if ((end * bits) % 8)\n throw new Error('padding: invalid, string should have whole number of bytes');\n for (; end > 0 && input[end - 1] === chr; end--) {\n const last = end - 1;\n const byte = last * bits;\n if (byte % 8 === 0)\n throw new Error('padding: invalid, string has too much padding');\n }\n return input.slice(0, end);\n },\n };\n}\n/**\n * @__NO_SIDE_EFFECTS__\n */\nfunction normalize(fn) {\n afn(fn);\n return { encode: (from) => from, decode: (to) => fn(to) };\n}\n/**\n * Slow: O(n^2) time complexity\n */\nfunction convertRadix(data, from, to) {\n // base 1 is impossible\n if (from < 2)\n throw new Error(`convertRadix: invalid from=${from}, base cannot be less than 2`);\n if (to < 2)\n throw new Erro