From 1ce91add10f5282f041c1dba323d76b2ef1840b6 Mon Sep 17 00:00:00 2001 From: Kerem Kazan Date: Wed, 8 Nov 2023 10:25:04 -0500 Subject: [PATCH 01/41] compiling the worker for typechecking purposes --- examples/nexus-worker/package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/nexus-worker/package.json b/examples/nexus-worker/package.json index bcf377f..c7484c3 100644 --- a/examples/nexus-worker/package.json +++ b/examples/nexus-worker/package.json @@ -8,7 +8,8 @@ }, "scripts": { "deploy": "wrangler deploy", - "dev": "wrangler dev --port 4005" + "dev": "wrangler dev --port 4005", + "build": "tsc" }, "dependencies": { "@whatsgood/nexus": "workspace:*" From 58f1e29e73a4011d8edfc32f86c6026fd06dd301 Mon Sep 17 00:00:00 2001 From: Kerem Kazan Date: Thu, 9 Nov 2023 10:06:12 -0500 Subject: [PATCH 02/41] exporting nexus as a standalone object --- examples/nexus-worker/src/index.ts | 10 ++++------ packages/nexus/src/config.ts | 2 +- packages/nexus/src/index.ts | 3 +-- packages/nexus/src/nexus/index.ts | 1 + packages/nexus/src/nexus/nexus.ts | 17 +++++++++++++++++ .../request-handler.relay.test.ts} | 6 +++--- .../request-handler.status.test.ts} | 8 ++++---- .../request-handler.ts} | 2 +- .../rpc-proxy-context.ts | 0 9 files changed, 32 insertions(+), 17 deletions(-) create mode 100644 packages/nexus/src/nexus/index.ts create mode 100644 packages/nexus/src/nexus/nexus.ts rename packages/nexus/src/{rpc-proxy-response-handler/rpc-proxy-response-handler.relay.test.ts => request-handler/request-handler.relay.test.ts} (95%) rename packages/nexus/src/{rpc-proxy-response-handler/rpc-proxy-response-handler.status.test.ts => request-handler/request-handler.status.test.ts} (95%) rename packages/nexus/src/{rpc-proxy-response-handler/rpc-proxy-response-handler.ts => request-handler/request-handler.ts} (98%) rename packages/nexus/src/{rpc-proxy-response-handler => request-handler}/rpc-proxy-context.ts (100%) diff --git a/examples/nexus-worker/src/index.ts b/examples/nexus-worker/src/index.ts index a991a11..e6d40e2 100644 --- a/examples/nexus-worker/src/index.ts +++ b/examples/nexus-worker/src/index.ts @@ -1,19 +1,17 @@ -import { Config, RpcProxyResponseHandler } from "@whatsgood/nexus"; +import { Nexus } from "@whatsgood/nexus"; // TODO: add config alerts to indicate that the key access is incomplete // TODO: add onboarding & UX. (setup admin access, login, etc) +// TODO: add tests for the worker export default { async fetch( request: Request, env: Record ): Promise { - const config = new Config({ + const nexus = new Nexus({ env, }); - const responseHandler = new RpcProxyResponseHandler({ - config, - }); - return responseHandler.handle(request); + return nexus.requestHandler.handle(request); }, }; diff --git a/packages/nexus/src/config.ts b/packages/nexus/src/config.ts index faf8269..7d3c6ca 100644 --- a/packages/nexus/src/config.ts +++ b/packages/nexus/src/config.ts @@ -14,7 +14,7 @@ interface ProviderConfig { type Env = Partial>; -interface ConfigConstructorParams extends Partial { +export interface ConfigConstructorParams extends Partial { // some runtimes don't support process.env (e.g. Cloudflare Workers) // so we allow them to pass in an object with the same keys as process.env env?: Env; diff --git a/packages/nexus/src/index.ts b/packages/nexus/src/index.ts index 86aa5b3..117a123 100644 --- a/packages/nexus/src/index.ts +++ b/packages/nexus/src/index.ts @@ -1,6 +1,5 @@ // TODO: how can i let the user specify the load balancing weights per service provider or even per custom provider url? // TODO: how do i build fallback logic? for example, `if ankr fails, fallback to infura`? -export * from "./rpc-endpoint/rpc-endpoint"; -export * from "./rpc-proxy-response-handler/rpc-proxy-response-handler"; export * from "./config"; +export * from "./nexus"; diff --git a/packages/nexus/src/nexus/index.ts b/packages/nexus/src/nexus/index.ts new file mode 100644 index 0000000..34e521f --- /dev/null +++ b/packages/nexus/src/nexus/index.ts @@ -0,0 +1 @@ +export { Nexus } from "./nexus"; diff --git a/packages/nexus/src/nexus/nexus.ts b/packages/nexus/src/nexus/nexus.ts new file mode 100644 index 0000000..8ff0701 --- /dev/null +++ b/packages/nexus/src/nexus/nexus.ts @@ -0,0 +1,17 @@ +import { Config } from "../config"; +import type { ConfigConstructorParams } from "../config"; +import { RequestHandler } from "../request-handler/request-handler"; + +export class Nexus { + private readonly config: Config; + + public readonly requestHandler: RequestHandler; + + constructor(params: ConfigConstructorParams) { + this.config = new Config(params); + this.requestHandler = new RequestHandler({ + config: this.config, + // TODO: make the chain registry part of the config + }); + } +} diff --git a/packages/nexus/src/rpc-proxy-response-handler/rpc-proxy-response-handler.relay.test.ts b/packages/nexus/src/request-handler/request-handler.relay.test.ts similarity index 95% rename from packages/nexus/src/rpc-proxy-response-handler/rpc-proxy-response-handler.relay.test.ts rename to packages/nexus/src/request-handler/request-handler.relay.test.ts index 10eee68..197a74d 100644 --- a/packages/nexus/src/rpc-proxy-response-handler/rpc-proxy-response-handler.relay.test.ts +++ b/packages/nexus/src/request-handler/request-handler.relay.test.ts @@ -3,7 +3,7 @@ import { setupServer } from "msw/node"; import { Config } from "../config"; import { handlers } from "../../tests/mock-server-handlers"; import { retry } from "../../tests/utils"; -import { RpcProxyResponseHandler } from "./rpc-proxy-response-handler"; +import { RequestHandler } from "./request-handler"; const sharedConfig = { globalAccessKey: "some-key", @@ -31,7 +31,7 @@ const configWithNoRecovery = new Config({ }); const blockNumberRequestHelper = (config: Config) => { - const requestHandler = new RpcProxyResponseHandler({ config }); + const requestHandler = new RequestHandler({ config }); const request = new Request( "https://my-test-rpc-provider.com/eth/mainnet?key=some-key", { @@ -48,7 +48,7 @@ const blockNumberRequestHelper = (config: Config) => { return requestHandler.handle(request); }; -describe("rpc proxy response handler - relay", () => { +describe("request handler - relay", () => { describe("all providers up", () => { const server = setupServer( handlers.alchemyReturnsBlockNumber, diff --git a/packages/nexus/src/rpc-proxy-response-handler/rpc-proxy-response-handler.status.test.ts b/packages/nexus/src/request-handler/request-handler.status.test.ts similarity index 95% rename from packages/nexus/src/rpc-proxy-response-handler/rpc-proxy-response-handler.status.test.ts rename to packages/nexus/src/request-handler/request-handler.status.test.ts index ce0d6a3..07090fc 100644 --- a/packages/nexus/src/rpc-proxy-response-handler/rpc-proxy-response-handler.status.test.ts +++ b/packages/nexus/src/request-handler/request-handler.status.test.ts @@ -2,20 +2,20 @@ import { afterAll, afterEach, beforeAll, describe, expect, it } from "vitest"; import { setupServer } from "msw/node"; import { Config } from "../config"; import { handlers } from "../../tests/mock-server-handlers"; -import { RpcProxyResponseHandler } from "./rpc-proxy-response-handler"; +import { RequestHandler } from "./request-handler"; export const requestHelper = async (endpoint: string, config: Config) => { - const rpcProxyResponseHandler = new RpcProxyResponseHandler({ config }); + const requestHandler = new RequestHandler({ config }); const request = new Request(`https://my-test-rpc-provider.com${endpoint}`, { method: "GET", }); - const response = await rpcProxyResponseHandler.handle(request); + const response = await requestHandler.handle(request); const data: unknown = await response.json(); return data; }; -describe("rpc proxy response handler - status", () => { +describe("request handler - status", () => { describe("providers are up", () => { const server = setupServer( handlers.alchemyReturnsBlockNumber, diff --git a/packages/nexus/src/rpc-proxy-response-handler/rpc-proxy-response-handler.ts b/packages/nexus/src/request-handler/request-handler.ts similarity index 98% rename from packages/nexus/src/rpc-proxy-response-handler/rpc-proxy-response-handler.ts rename to packages/nexus/src/request-handler/request-handler.ts index 9ad5e03..482b958 100644 --- a/packages/nexus/src/rpc-proxy-response-handler/rpc-proxy-response-handler.ts +++ b/packages/nexus/src/request-handler/request-handler.ts @@ -5,7 +5,7 @@ import type { ChainRegistry } from "../chain/chain-registry"; import { defaultChainRegistry } from "../setup/data"; import { RpcProxyResponseContext } from "./rpc-proxy-context"; -export class RpcProxyResponseHandler { +export class RequestHandler { private readonly endpointFactory: RpcEndpointPoolFactory; private readonly chainRegistry: ChainRegistry; private readonly config: Config; diff --git a/packages/nexus/src/rpc-proxy-response-handler/rpc-proxy-context.ts b/packages/nexus/src/request-handler/rpc-proxy-context.ts similarity index 100% rename from packages/nexus/src/rpc-proxy-response-handler/rpc-proxy-context.ts rename to packages/nexus/src/request-handler/rpc-proxy-context.ts From b724ad645c7f6f1528234a3adf3cfa34ed399f95 Mon Sep 17 00:00:00 2001 From: Kerem Kazan Date: Fri, 10 Nov 2023 07:52:54 -0500 Subject: [PATCH 03/41] moved json rpc types to own file --- .../nexus/src/rpc-endpoint/json-rpc-types.ts | 16 ++++++++++++++++ .../nexus/src/rpc-endpoint/rpc-endpoint.ts | 19 +++---------------- 2 files changed, 19 insertions(+), 16 deletions(-) create mode 100644 packages/nexus/src/rpc-endpoint/json-rpc-types.ts diff --git a/packages/nexus/src/rpc-endpoint/json-rpc-types.ts b/packages/nexus/src/rpc-endpoint/json-rpc-types.ts new file mode 100644 index 0000000..9099496 --- /dev/null +++ b/packages/nexus/src/rpc-endpoint/json-rpc-types.ts @@ -0,0 +1,16 @@ +import { z } from "zod"; + +// TODO: migrate to the older whatsgood rpc-proxy schema validation, +// based on the input params. +export const JsonRPCResponseSchema = z.object({ + jsonrpc: z.literal("2.0"), + id: z.number(), + result: z.any(), +}); + +export const JsonRPCRequestSchema = z.object({ + jsonrpc: z.literal("2.0"), + id: z.number(), + method: z.string(), + params: z.array(z.any()), +}); diff --git a/packages/nexus/src/rpc-endpoint/rpc-endpoint.ts b/packages/nexus/src/rpc-endpoint/rpc-endpoint.ts index d7ef827..efcdd20 100644 --- a/packages/nexus/src/rpc-endpoint/rpc-endpoint.ts +++ b/packages/nexus/src/rpc-endpoint/rpc-endpoint.ts @@ -1,21 +1,8 @@ -import { z } from "zod"; +import type { z } from "zod"; import type { Chain } from "../chain/chain"; import type { ServiceProvider } from "../service-provider/service-provider"; - -// TODO: migrate to the older whatsgood rpc-proxy schema validation, -// based on the input params. -const JsonRPCResponseSchema = z.object({ - jsonrpc: z.literal("2.0"), - id: z.number(), - result: z.any(), -}); - -export const JsonRPCRequestSchema = z.object({ - jsonrpc: z.literal("2.0"), - id: z.number(), - method: z.string(), - params: z.array(z.any()), -}); +import type { JsonRPCRequestSchema } from "./json-rpc-types"; +import { JsonRPCResponseSchema } from "./json-rpc-types"; export class RpcEndpoint { public readonly url: string; From 26d92d9bb320d903b7aa1aaab89917f72293fe98 Mon Sep 17 00:00:00 2001 From: Kerem Kazan Date: Fri, 10 Nov 2023 07:53:55 -0500 Subject: [PATCH 04/41] class renamed to RpcProxyContext --- .../nexus/src/request-handler/request-handler.ts | 14 +++++--------- .../nexus/src/request-handler/rpc-proxy-context.ts | 2 +- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/packages/nexus/src/request-handler/request-handler.ts b/packages/nexus/src/request-handler/request-handler.ts index 482b958..249373a 100644 --- a/packages/nexus/src/request-handler/request-handler.ts +++ b/packages/nexus/src/request-handler/request-handler.ts @@ -3,7 +3,7 @@ import { matchPath } from "../routes/routes"; import { RpcEndpointPoolFactory } from "../rpc-endpoint/rpc-endpoint-pool-factory"; import type { ChainRegistry } from "../chain/chain-registry"; import { defaultChainRegistry } from "../setup/data"; -import { RpcProxyResponseContext } from "./rpc-proxy-context"; +import { RpcProxyContext } from "./rpc-proxy-context"; export class RequestHandler { private readonly endpointFactory: RpcEndpointPoolFactory; @@ -18,7 +18,7 @@ export class RequestHandler { this.chainRegistry = params.chainRegistry ?? defaultChainRegistry; } - private buildContext(request: Request): RpcProxyResponseContext { + private buildContext(request: Request): RpcProxyContext { const requestUrl = new URL(request.url); const route = matchPath(requestUrl.pathname); @@ -27,7 +27,7 @@ export class RequestHandler { : undefined; const pool = chain ? this.endpointFactory.fromChain(chain) : undefined; - return new RpcProxyResponseContext({ + return new RpcProxyContext({ pool, config: this.config, chain, @@ -35,9 +35,7 @@ export class RequestHandler { }); } - private async handleStatus( - context: RpcProxyResponseContext - ): Promise { + private async handleStatus(context: RpcProxyContext): Promise { const status = await context.getStatus(); return Response.json(status, { @@ -45,9 +43,7 @@ export class RequestHandler { }); } - private async handleRpcRelay( - context: RpcProxyResponseContext - ): Promise { + private async handleRpcRelay(context: RpcProxyContext): Promise { if (!context.pool) { return new Response("Unexpected Error: Not Found", { status: 500 }); } diff --git a/packages/nexus/src/request-handler/rpc-proxy-context.ts b/packages/nexus/src/request-handler/rpc-proxy-context.ts index 6ea4058..8062e71 100644 --- a/packages/nexus/src/request-handler/rpc-proxy-context.ts +++ b/packages/nexus/src/request-handler/rpc-proxy-context.ts @@ -25,7 +25,7 @@ interface ErrorStatus extends BaseStatus { type Status = SuccessStatus | ErrorStatus; -export class RpcProxyResponseContext { +export class RpcProxyContext { public readonly chain?: Chain; public readonly pool?: RpcEndpointPool; From b4ec1a906a557f4c23115d6d076a1d10bd0ff2f1 Mon Sep 17 00:00:00 2001 From: Kerem Kazan Date: Fri, 10 Nov 2023 07:56:07 -0500 Subject: [PATCH 05/41] fixed bad import --- packages/nexus/src/rpc-endpoint/rpc-endpoint-pool.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/nexus/src/rpc-endpoint/rpc-endpoint-pool.ts b/packages/nexus/src/rpc-endpoint/rpc-endpoint-pool.ts index ec6a926..a5f3c59 100644 --- a/packages/nexus/src/rpc-endpoint/rpc-endpoint-pool.ts +++ b/packages/nexus/src/rpc-endpoint/rpc-endpoint-pool.ts @@ -1,7 +1,8 @@ import type { Config } from "../config"; import type { Chain } from "../chain/chain"; import type { ServiceProvider } from "../service-provider/service-provider"; -import { JsonRPCRequestSchema, type RpcEndpoint } from "./rpc-endpoint"; +import { JsonRPCRequestSchema } from "./json-rpc-types"; +import type { RpcEndpoint } from "./rpc-endpoint"; export class RpcEndpointPool { public readonly chain: Chain; From fc19dd5bdb1918008f11352e04e400c296e5d9a4 Mon Sep 17 00:00:00 2001 From: Kerem Kazan Date: Fri, 10 Nov 2023 07:56:41 -0500 Subject: [PATCH 06/41] request -> relay --- packages/nexus/src/rpc-endpoint/rpc-endpoint-pool.ts | 4 ++-- packages/nexus/src/rpc-endpoint/rpc-endpoint.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/nexus/src/rpc-endpoint/rpc-endpoint-pool.ts b/packages/nexus/src/rpc-endpoint/rpc-endpoint-pool.ts index a5f3c59..d95cafa 100644 --- a/packages/nexus/src/rpc-endpoint/rpc-endpoint-pool.ts +++ b/packages/nexus/src/rpc-endpoint/rpc-endpoint-pool.ts @@ -118,7 +118,7 @@ export class RpcEndpointPool { // TODO: what should we return if no endpoint is available? if (this.config.recoveryMode === "none") { return ( - this.current?.req(parsedPayload.data) ?? + this.current?.relay(parsedPayload.data) ?? new Response(null, { status: 500 }) ); // TODO: standardize these Response objects @@ -133,7 +133,7 @@ export class RpcEndpointPool { break; } - const response = await endpoint.req(parsedPayload.data); + const response = await endpoint.relay(parsedPayload.data); if (response.type === "success") { return response; diff --git a/packages/nexus/src/rpc-endpoint/rpc-endpoint.ts b/packages/nexus/src/rpc-endpoint/rpc-endpoint.ts index efcdd20..fcf3c7f 100644 --- a/packages/nexus/src/rpc-endpoint/rpc-endpoint.ts +++ b/packages/nexus/src/rpc-endpoint/rpc-endpoint.ts @@ -43,7 +43,7 @@ export class RpcEndpoint { } } - public async req(request: z.TypeOf) { + public async relay(request: z.TypeOf) { // TODO: add caching for non-mutating requests // TODO: test that the payload from the client actually hits the relayed server From 2e9572e458691742542ddca0537b5a9852d75e05 Mon Sep 17 00:00:00 2001 From: Kerem Kazan Date: Fri, 10 Nov 2023 07:58:02 -0500 Subject: [PATCH 07/41] using the jsonrpcrequest type --- packages/nexus/src/rpc-endpoint/json-rpc-types.ts | 3 +++ packages/nexus/src/rpc-endpoint/rpc-endpoint.ts | 5 ++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/nexus/src/rpc-endpoint/json-rpc-types.ts b/packages/nexus/src/rpc-endpoint/json-rpc-types.ts index 9099496..82348e3 100644 --- a/packages/nexus/src/rpc-endpoint/json-rpc-types.ts +++ b/packages/nexus/src/rpc-endpoint/json-rpc-types.ts @@ -14,3 +14,6 @@ export const JsonRPCRequestSchema = z.object({ method: z.string(), params: z.array(z.any()), }); + +export type JsonRPCRequest = z.infer; +export type JsonRPCResponse = z.infer; diff --git a/packages/nexus/src/rpc-endpoint/rpc-endpoint.ts b/packages/nexus/src/rpc-endpoint/rpc-endpoint.ts index fcf3c7f..9323d49 100644 --- a/packages/nexus/src/rpc-endpoint/rpc-endpoint.ts +++ b/packages/nexus/src/rpc-endpoint/rpc-endpoint.ts @@ -1,7 +1,6 @@ -import type { z } from "zod"; import type { Chain } from "../chain/chain"; import type { ServiceProvider } from "../service-provider/service-provider"; -import type { JsonRPCRequestSchema } from "./json-rpc-types"; +import type { JsonRPCRequest } from "./json-rpc-types"; import { JsonRPCResponseSchema } from "./json-rpc-types"; export class RpcEndpoint { @@ -43,7 +42,7 @@ export class RpcEndpoint { } } - public async relay(request: z.TypeOf) { + public async relay(request: JsonRPCRequest) { // TODO: add caching for non-mutating requests // TODO: test that the payload from the client actually hits the relayed server From b779bca50ca68676b099b91aaf82fde318ec692d Mon Sep 17 00:00:00 2001 From: Kerem Kazan Date: Fri, 10 Nov 2023 08:15:46 -0500 Subject: [PATCH 08/41] fetch Request type removed from internals --- .../src/request-handler/request-handler.ts | 71 +++++++++++++++++-- .../src/request-handler/rpc-proxy-context.ts | 23 +++--- .../src/rpc-endpoint/rpc-endpoint-pool.ts | 46 ++---------- 3 files changed, 85 insertions(+), 55 deletions(-) diff --git a/packages/nexus/src/request-handler/request-handler.ts b/packages/nexus/src/request-handler/request-handler.ts index 249373a..5d67c7c 100644 --- a/packages/nexus/src/request-handler/request-handler.ts +++ b/packages/nexus/src/request-handler/request-handler.ts @@ -3,6 +3,7 @@ import { matchPath } from "../routes/routes"; import { RpcEndpointPoolFactory } from "../rpc-endpoint/rpc-endpoint-pool-factory"; import type { ChainRegistry } from "../chain/chain-registry"; import { defaultChainRegistry } from "../setup/data"; +import { JsonRPCRequestSchema } from "../rpc-endpoint/json-rpc-types"; import { RpcProxyContext } from "./rpc-proxy-context"; export class RequestHandler { @@ -18,8 +19,54 @@ export class RequestHandler { this.chainRegistry = params.chainRegistry ?? defaultChainRegistry; } - private buildContext(request: Request): RpcProxyContext { + private async parseJSONRpcRequest(request: Request) { + // we clean the request to remove any non-required pieces + let payload: unknown; + + try { + payload = await request.json(); + } catch (error) { + console.error( + JSON.stringify( + { + request, + error, + }, + null, + 2 + ) + ); + + return { + type: "invalid-json-request", + request, + error, + } as const; + } + + const parsedPayload = JsonRPCRequestSchema.safeParse(payload); + + if (!parsedPayload.success) { + console.error(parsedPayload.error); + + return { + type: "invalid-json-rpc-request", + request, + payload, + error: parsedPayload.error, + } as const; + } + + return { + type: "success", + request, + data: parsedPayload.data, + } as const; + } + + private async buildContext(request: Request): Promise { const requestUrl = new URL(request.url); + const requestPath = requestUrl.pathname; const route = matchPath(requestUrl.pathname); const chain = route @@ -27,11 +74,20 @@ export class RequestHandler { : undefined; const pool = chain ? this.endpointFactory.fromChain(chain) : undefined; + const jsonRPCRequestParseResult = await this.parseJSONRpcRequest(request); + + const clientAccessKey = requestUrl.searchParams.get("key") || undefined; + return new RpcProxyContext({ pool, config: this.config, chain, - request, + path: requestPath, + clientAccessKey, + jsonRPCRequest: + jsonRPCRequestParseResult.type === "success" + ? jsonRPCRequestParseResult.data + : undefined, }); } @@ -44,11 +100,18 @@ export class RequestHandler { } private async handleRpcRelay(context: RpcProxyContext): Promise { + if (!context.jsonRPCRequest) { + // TODO: test + // TODO: pass the actual parse result into the context, not the + // jsonRPCRequest + return new Response("Invalid Json RPC Request ", { status: 400 }); + } + if (!context.pool) { return new Response("Unexpected Error: Not Found", { status: 500 }); } - const result = await context.pool.relay(context.request); + const result = await context.pool.relay(context.jsonRPCRequest); if (result.type === "success") { return Response.json(result.data); @@ -65,7 +128,7 @@ export class RequestHandler { } public async handle(request: Request): Promise { - const context = this.buildContext(request); + const context = await this.buildContext(request); if (request.method === "GET") { return this.handleStatus(context); diff --git a/packages/nexus/src/request-handler/rpc-proxy-context.ts b/packages/nexus/src/request-handler/rpc-proxy-context.ts index 8062e71..757512b 100644 --- a/packages/nexus/src/request-handler/rpc-proxy-context.ts +++ b/packages/nexus/src/request-handler/rpc-proxy-context.ts @@ -1,6 +1,7 @@ import type { Config } from "../config"; import type { RpcEndpointPool } from "../rpc-endpoint/rpc-endpoint-pool"; import type { Chain, ChainStatus } from "../chain/chain"; +import type { JsonRPCRequest } from "../rpc-endpoint/json-rpc-types"; type Access = "unprotected" | "unauthorized" | "authorized"; @@ -8,7 +9,7 @@ interface BaseStatus { success: boolean; message: string; code: number; - url: string; + path: string; } interface SuccessStatus extends BaseStatus { @@ -29,19 +30,26 @@ export class RpcProxyContext { public readonly chain?: Chain; public readonly pool?: RpcEndpointPool; - public readonly request: Request; + public readonly jsonRPCRequest?: JsonRPCRequest; private readonly config: Config; + public readonly path: string; + + private readonly clientAccessKey?: string; constructor(params: { pool?: RpcEndpointPool; chain?: Chain; config: Config; - request: Request; + jsonRPCRequest?: JsonRPCRequest; + path: string; + clientAccessKey?: string; }) { this.chain = params.chain; this.config = params.config; - this.request = params.request; + this.jsonRPCRequest = params.jsonRPCRequest; this.pool = params.pool; + this.path = params.path; + this.clientAccessKey = params.clientAccessKey; } private buildStatus(params: { @@ -57,7 +65,7 @@ export class RpcProxyContext { success: params.success, message: params.message, code: params.code, - url: this.request.url.toString(), + path: this.path, }; const access = this.getAccess(); @@ -82,12 +90,9 @@ export class RpcProxyContext { } private getAccess(): Access { - const requestUrl = new URL(this.request.url); - const clientAccessKey = requestUrl.searchParams.get("key"); - if (!this.config.globalAccessKey) { return "unprotected"; - } else if (clientAccessKey === this.config.globalAccessKey) { + } else if (this.clientAccessKey === this.config.globalAccessKey) { return "authorized"; } diff --git a/packages/nexus/src/rpc-endpoint/rpc-endpoint-pool.ts b/packages/nexus/src/rpc-endpoint/rpc-endpoint-pool.ts index d95cafa..79b6fce 100644 --- a/packages/nexus/src/rpc-endpoint/rpc-endpoint-pool.ts +++ b/packages/nexus/src/rpc-endpoint/rpc-endpoint-pool.ts @@ -1,7 +1,7 @@ import type { Config } from "../config"; import type { Chain } from "../chain/chain"; import type { ServiceProvider } from "../service-provider/service-provider"; -import { JsonRPCRequestSchema } from "./json-rpc-types"; +import type { JsonRPCRequest } from "./json-rpc-types"; import type { RpcEndpoint } from "./rpc-endpoint"; export class RpcEndpointPool { @@ -77,49 +77,11 @@ export class RpcEndpointPool { return false; } - public async relay(request: Request) { - // we clean the request to remove any non-required pieces - let payload: unknown; - - try { - payload = await request.json(); - } catch (error) { - console.error( - JSON.stringify( - { - request, - error, - }, - null, - 2 - ) - ); - - return { - type: "invalid-json-request", - request, - error, - } as const; - } - - const parsedPayload = JsonRPCRequestSchema.safeParse(payload); - - if (!parsedPayload.success) { - console.error(parsedPayload.error); - - return { - type: "invalid-json-rpc-request", - request, - payload, - error: parsedPayload.error, - } as const; - } - + public async relay(request: JsonRPCRequest) { // TODO: what should we return if no endpoint is available? if (this.config.recoveryMode === "none") { return ( - this.current?.relay(parsedPayload.data) ?? - new Response(null, { status: 500 }) + this.current?.relay(request) ?? new Response(null, { status: 500 }) ); // TODO: standardize these Response objects } @@ -133,7 +95,7 @@ export class RpcEndpointPool { break; } - const response = await endpoint.relay(parsedPayload.data); + const response = await endpoint.relay(request); if (response.type === "success") { return response; From 381a574550c284b115af504f7c88565eb45cb455 Mon Sep 17 00:00:00 2001 From: Kerem Kazan Date: Fri, 10 Nov 2023 08:17:58 -0500 Subject: [PATCH 09/41] changed 500 error message --- packages/nexus/src/request-handler/request-handler.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/nexus/src/request-handler/request-handler.ts b/packages/nexus/src/request-handler/request-handler.ts index 5d67c7c..cdb53c7 100644 --- a/packages/nexus/src/request-handler/request-handler.ts +++ b/packages/nexus/src/request-handler/request-handler.ts @@ -108,7 +108,9 @@ export class RequestHandler { } if (!context.pool) { - return new Response("Unexpected Error: Not Found", { status: 500 }); + return new Response("Unexpected Error: Pool unitialized", { + status: 500, + }); } const result = await context.pool.relay(context.jsonRPCRequest); From 061fd47f6e3333a345cacce2214141459a066887 Mon Sep 17 00:00:00 2001 From: Kerem Kazan Date: Fri, 10 Nov 2023 10:07:16 -0500 Subject: [PATCH 10/41] default config param to nexus --- packages/nexus/src/nexus/nexus.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/nexus/src/nexus/nexus.ts b/packages/nexus/src/nexus/nexus.ts index 8ff0701..d797bef 100644 --- a/packages/nexus/src/nexus/nexus.ts +++ b/packages/nexus/src/nexus/nexus.ts @@ -7,7 +7,7 @@ export class Nexus { public readonly requestHandler: RequestHandler; - constructor(params: ConfigConstructorParams) { + constructor(params: ConfigConstructorParams = {}) { this.config = new Config(params); this.requestHandler = new RequestHandler({ config: this.config, From 90512e297d919ea619b69a68bf2f0b387c9fa284 Mon Sep 17 00:00:00 2001 From: Kerem Kazan Date: Fri, 10 Nov 2023 10:07:51 -0500 Subject: [PATCH 11/41] application/json header added to relayed request --- packages/nexus/src/rpc-endpoint/rpc-endpoint.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/nexus/src/rpc-endpoint/rpc-endpoint.ts b/packages/nexus/src/rpc-endpoint/rpc-endpoint.ts index 9323d49..73c7def 100644 --- a/packages/nexus/src/rpc-endpoint/rpc-endpoint.ts +++ b/packages/nexus/src/rpc-endpoint/rpc-endpoint.ts @@ -49,6 +49,9 @@ export class RpcEndpoint { const cleanedRequest = new Request(this.url, { body: JSON.stringify(request), method: "POST", + headers: { + "Content-Type": "application/json", + }, }); let relayResponse: Response; From 031a2a5e456980a9224f75301b79420f17216960 Mon Sep 17 00:00:00 2001 From: Kerem Kazan Date: Fri, 10 Nov 2023 11:42:42 -0500 Subject: [PATCH 12/41] fetch + node request handlers separated --- README.md | 12 +- docs/recipes/cloudflare-worker.mdx | 10 +- examples/nexus-worker/src/index.ts | 2 +- .../request-handler.relay.test.ts | 2 +- .../request-handler.status.test.ts | 2 +- .../src/request-handler/request-handler.ts | 233 +++++++++++++++--- .../src/request-handler/rpc-proxy-context.ts | 61 +++++ 7 files changed, 273 insertions(+), 49 deletions(-) diff --git a/README.md b/README.md index fac9e4d..7fe0dd1 100644 --- a/README.md +++ b/README.md @@ -48,21 +48,21 @@ yarn add @whatsgood/nexus ```ts // Cloudflare worker example -import { Config, RpcProxyResponseHandler } from "@whatsgood/nexus"; +import { Nexus } from "@whatsgood/nexus"; export default { async fetch( request: Request, env: Record ): Promise { - const config = new Config({ + const nexus = new Nexus({ env, }); - const responseHandler = new RpcProxyResponseHandler({ - config, - }); - return responseHandler.handle(request); + return nexus.requestHandler.handleFetch(request); }, }; + ``` + + \ No newline at end of file diff --git a/docs/recipes/cloudflare-worker.mdx b/docs/recipes/cloudflare-worker.mdx index 1576719..8dfb064 100644 --- a/docs/recipes/cloudflare-worker.mdx +++ b/docs/recipes/cloudflare-worker.mdx @@ -303,22 +303,20 @@ For the purposes of this tutorial, update the `"dev"` script in `package.json` f ```ts src/index.ts -import { Config, RpcProxyResponseHandler } from "@whatsgood/nexus"; +import { Nexus } from "@whatsgood/nexus"; export default { async fetch( request: Request, env: Record ): Promise { - const config = new Config({ + const nexus = new Nexus({ env, }); - const responseHandler = new RpcProxyResponseHandler({ - config, - }); - return responseHandler.handle(request); + return nexus.requestHandler.handleFetch(request); }, }; + ``` ```json package.json diff --git a/examples/nexus-worker/src/index.ts b/examples/nexus-worker/src/index.ts index e6d40e2..d22de50 100644 --- a/examples/nexus-worker/src/index.ts +++ b/examples/nexus-worker/src/index.ts @@ -12,6 +12,6 @@ export default { const nexus = new Nexus({ env, }); - return nexus.requestHandler.handle(request); + return nexus.requestHandler.handleFetch(request); }, }; diff --git a/packages/nexus/src/request-handler/request-handler.relay.test.ts b/packages/nexus/src/request-handler/request-handler.relay.test.ts index 197a74d..4231f70 100644 --- a/packages/nexus/src/request-handler/request-handler.relay.test.ts +++ b/packages/nexus/src/request-handler/request-handler.relay.test.ts @@ -45,7 +45,7 @@ const blockNumberRequestHelper = (config: Config) => { } ); - return requestHandler.handle(request); + return requestHandler.handleFetch(request); }; describe("request handler - relay", () => { diff --git a/packages/nexus/src/request-handler/request-handler.status.test.ts b/packages/nexus/src/request-handler/request-handler.status.test.ts index 07090fc..7279f62 100644 --- a/packages/nexus/src/request-handler/request-handler.status.test.ts +++ b/packages/nexus/src/request-handler/request-handler.status.test.ts @@ -9,7 +9,7 @@ export const requestHelper = async (endpoint: string, config: Config) => { const request = new Request(`https://my-test-rpc-provider.com${endpoint}`, { method: "GET", }); - const response = await requestHandler.handle(request); + const response = await requestHandler.handleFetch(request); const data: unknown = await response.json(); return data; diff --git a/packages/nexus/src/request-handler/request-handler.ts b/packages/nexus/src/request-handler/request-handler.ts index cdb53c7..6061ab7 100644 --- a/packages/nexus/src/request-handler/request-handler.ts +++ b/packages/nexus/src/request-handler/request-handler.ts @@ -1,3 +1,6 @@ +import type * as http from "node:http"; +import NodeURL from "node:url"; +import querystring from "node:querystring"; import type { Config } from "../config"; import { matchPath } from "../routes/routes"; import { RpcEndpointPoolFactory } from "../rpc-endpoint/rpc-endpoint-pool-factory"; @@ -19,7 +22,116 @@ export class RequestHandler { this.chainRegistry = params.chainRegistry ?? defaultChainRegistry; } - private async parseJSONRpcRequest(request: Request) { + private parseJSONFromNodeHttpRequest(req: http.IncomingMessage) { + // TODO: is any the right choice? + // TODO: test + const body: any[] = []; + let receivedSize = 0; + const maxSize = 1e6; // 1 MB, for example + + return new Promise((resolve, reject) => { + req.on("data", (chunk) => { + receivedSize += chunk.length; + + if (receivedSize > maxSize) { + reject(new Error("Payload too large")); + req.destroy(); + } else { + body.push(chunk); + } + }); + + req.on("end", () => { + try { + const parsedBody: unknown = JSON.parse( + Buffer.concat(body).toString() + ); + + resolve(parsedBody); + } catch (error) { + reject(new Error("Invalid JSON")); + } + }); + + req.on("error", (error) => { + reject(error); + }); + }); + } + + private async parseJSONRpcRequestFromNodeHttpRequest( + req: http.IncomingMessage + ) { + let payload: unknown; + + try { + // TODO: cover the cases where the request is not a json rpc request + // TODO: cover the cases where the request is already parsed as a json object + // from a higher middleware + payload = await this.parseJSONFromNodeHttpRequest(req); + } catch (error) { + console.error( + JSON.stringify( + { + req, + error, + }, + null, + 2 + ) + ); + + return { + type: "invalid-json-request", + req, + error, + } as const; + } + + try { + console.log("PAYLOAD:", payload); + + const parsedPayload = JsonRPCRequestSchema.safeParse(payload); + + if (!parsedPayload.success) { + console.log("HELLO!"); + console.log("error parsing json rpc request", payload); + console.error(parsedPayload.error); + + return { + type: "invalid-json-rpc-request", + req, + payload, + error: parsedPayload.error, + } as const; + } + + return { + type: "success", + req, + data: parsedPayload.data, + } as const; + } catch (error) { + console.error( + JSON.stringify( + { + req, + error, + }, + null, + 2 + ) + ); + + return { + type: "invalid-json-request", + req, + error, + } as const; + } + } + + private async parseJSONRpcRequestFromFetchRequest(request: Request) { // we clean the request to remove any non-required pieces let payload: unknown; @@ -64,7 +176,9 @@ export class RequestHandler { } as const; } - private async buildContext(request: Request): Promise { + private async buildContextFromFetchRequest( + request: Request + ): Promise { const requestUrl = new URL(request.url); const requestPath = requestUrl.pathname; @@ -74,7 +188,8 @@ export class RequestHandler { : undefined; const pool = chain ? this.endpointFactory.fromChain(chain) : undefined; - const jsonRPCRequestParseResult = await this.parseJSONRpcRequest(request); + const jsonRPCRequestParseResult = + await this.parseJSONRpcRequestFromFetchRequest(request); const clientAccessKey = requestUrl.searchParams.get("key") || undefined; @@ -91,51 +206,66 @@ export class RequestHandler { }); } - private async handleStatus(context: RpcProxyContext): Promise { - const status = await context.getStatus(); - - return Response.json(status, { - status: status.code, - }); - } + private async buildContextFromNodeHttpRequest( + req: http.IncomingMessage + ): Promise { + // TODO: test node http requests + console.log("URL:", req.url); + const requestUrl = NodeURL.parse(req.url || ""); + const requestPath = requestUrl.pathname; + const { query } = requestUrl; - private async handleRpcRelay(context: RpcProxyContext): Promise { - if (!context.jsonRPCRequest) { - // TODO: test - // TODO: pass the actual parse result into the context, not the - // jsonRPCRequest - return new Response("Invalid Json RPC Request ", { status: 400 }); - } + const route = requestPath ? matchPath(requestPath) : undefined; + const chain = route + ? this.chainRegistry.getByOptionalParams(route.params) + : undefined; + const pool = chain ? this.endpointFactory.fromChain(chain) : undefined; - if (!context.pool) { - return new Response("Unexpected Error: Pool unitialized", { - status: 500, - }); - } + console.log({ + chain, + route, + pool, + }); - const result = await context.pool.relay(context.jsonRPCRequest); + const clientAccessKey = query ? querystring.parse(query).key : undefined; - if (result.type === "success") { - return Response.json(result.data); - } + const jsonRPCRequestParseResult = + await this.parseJSONRpcRequestFromNodeHttpRequest(req); - // TODO: should respod with a jsonrpc-compliant error + return new RpcProxyContext({ + pool, + config: this.config, + chain, + path: requestPath || "", // TODO: remove this hack, make this more robust + clientAccessKey: + typeof clientAccessKey === "string" ? clientAccessKey : undefined, + jsonRPCRequest: + jsonRPCRequestParseResult.type === "success" + ? jsonRPCRequestParseResult.data + : undefined, + }); + } + private async handleStatus(context: RpcProxyContext) { const status = await context.getStatus(); - // TODO: status should always fail here, given the response is not ok. - return Response.json(status, { + return { status: status.code, - }); + body: status, + }; } - public async handle(request: Request): Promise { - const context = await this.buildContext(request); + public async handleFetch(request: Request): Promise { + const context = await this.buildContextFromFetchRequest(request); if (request.method === "GET") { - return this.handleStatus(context); + const responseRaw = await this.handleStatus(context); + + return Response.json(responseRaw.body, { status: responseRaw.status }); } else if (request.method === "POST") { - return this.handleRpcRelay(context); + const result = await context.relay(); + + return Response.json(result.body, { status: result.status }); } return Response.json( @@ -145,4 +275,39 @@ export class RequestHandler { { status: 404 } ); } + + public async handleNodeHttp( + req: http.IncomingMessage, + res: http.ServerResponse + ) { + const context = await this.buildContextFromNodeHttpRequest(req); + + if (req.method === "GET") { + const status = await context.getStatus(); + + res.writeHead(status.code, { + "Content-Type": "application/json", + }); + + res.end(JSON.stringify(status)); + } else if (req.method === "POST") { + const result = await context.relay(); + + res.writeHead(result.status, { + "Content-Type": "application/json", + }); + + res.end(JSON.stringify(result.body)); + } else { + res.writeHead(404, { + "Content-Type": "application/json", + }); + + res.end( + JSON.stringify({ + message: "Not Found", + }) + ); + } + } } diff --git a/packages/nexus/src/request-handler/rpc-proxy-context.ts b/packages/nexus/src/request-handler/rpc-proxy-context.ts index 757512b..48e3cf1 100644 --- a/packages/nexus/src/request-handler/rpc-proxy-context.ts +++ b/packages/nexus/src/request-handler/rpc-proxy-context.ts @@ -31,6 +31,7 @@ export class RpcProxyContext { public readonly pool?: RpcEndpointPool; public readonly jsonRPCRequest?: JsonRPCRequest; + public relayResult?: Awaited>; private readonly config: Config; public readonly path: string; @@ -99,9 +100,69 @@ export class RpcProxyContext { return "unauthorized"; } + public async relay() { + if (!this.jsonRPCRequest) { + // TODO: test + // TODO: pass the actual parse result into the context, not the + // jsonRPCRequest + return { + status: 400, + body: { + message: "Invalid Json RPC Request", + }, + }; + } + + if (!this.pool) { + // TODO: cover the cases where there are no known providers configured + // to support the given chain + return { + status: 500, + body: { + message: "Unexpected Error: Pool unitialized", + }, + }; + } + + this.relayResult = await this.pool.relay(this.jsonRPCRequest); + + if (this.relayResult.type === "success") { + return { + status: 200, // TODO: should this be 200? or should it be adjusted based on the response? + body: this.relayResult.data, + }; + } + + // TODO: should respond with a jsonrpc-compliant error + + const status = await this.getStatus(); + + // TODO: status should always fail here, given the response is not ok. + return { + status: status.code, + body: { + message: status.message, + }, + }; + } + public async getStatus(): Promise { // TODO: cover malformed urls as well + if (this.relayResult?.type === "all-failed") { + // TODO: communicate the provider failures to the client + console.error( + "All providers failed to relay the request", + this.relayResult.errors + ); + + return this.buildStatus({ + message: "All providers failed.", + success: false, + code: 500, + }); + } + if (!this.pool) { if (!this.chain) { return this.buildStatus({ From acfaefde8ab2ce75637c6a721792c767ea7cc717 Mon Sep 17 00:00:00 2001 From: Kerem Kazan Date: Sun, 12 Nov 2023 09:02:58 -0500 Subject: [PATCH 13/41] console.warn added --- packages/nexus/src/rpc-endpoint/rpc-endpoint-pool.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/nexus/src/rpc-endpoint/rpc-endpoint-pool.ts b/packages/nexus/src/rpc-endpoint/rpc-endpoint-pool.ts index 79b6fce..bac6014 100644 --- a/packages/nexus/src/rpc-endpoint/rpc-endpoint-pool.ts +++ b/packages/nexus/src/rpc-endpoint/rpc-endpoint-pool.ts @@ -101,6 +101,11 @@ export class RpcEndpointPool { return response; } + console.warn( + `Provider: ${endpoint.provider.name} failed to relay request:`, + response + ); + errors.push(response); this.currentServiceProviderIndex++; } From e91eea92329dde883bc75d81a13c332755e1422a Mon Sep 17 00:00:00 2001 From: Kerem Kazan Date: Sun, 12 Nov 2023 12:40:27 -0500 Subject: [PATCH 14/41] separated node and fetch into own handlers --- .../request-handler/fetch-request-handler.ts | 126 +++++++ .../request-handler/node-request-handler.ts | 205 +++++++++++ .../src/request-handler/request-handler.ts | 338 +++--------------- .../src/request-handler/rpc-proxy-context.ts | 4 +- .../rpc-endpoint/rpc-endpoint-pool-factory.ts | 8 +- 5 files changed, 384 insertions(+), 297 deletions(-) create mode 100644 packages/nexus/src/request-handler/fetch-request-handler.ts create mode 100644 packages/nexus/src/request-handler/node-request-handler.ts diff --git a/packages/nexus/src/request-handler/fetch-request-handler.ts b/packages/nexus/src/request-handler/fetch-request-handler.ts new file mode 100644 index 0000000..cb1cc4e --- /dev/null +++ b/packages/nexus/src/request-handler/fetch-request-handler.ts @@ -0,0 +1,126 @@ +import type { ChainRegistry } from "../chain/chain-registry"; +import type { Config } from "../config"; +import { matchPath } from "../routes/routes"; +import { JsonRPCRequestSchema } from "../rpc-endpoint/json-rpc-types"; +import type { RpcEndpointPoolFactory } from "../rpc-endpoint/rpc-endpoint-pool-factory"; +import type { Nexus } from "../nexus"; +import { RpcProxyContext } from "./rpc-proxy-context"; +import type { NexusPreResponse } from "./request-handler"; +import { RequestHandler } from "./request-handler"; + +export class FetchRequestHandler extends RequestHandler { + private readonly request: Request; + + constructor(params: { + config: Config; + chainRegistry: ChainRegistry; + rpcEndpointPoolFactory: RpcEndpointPoolFactory; + request: Request; + }) { + super(params); + this.request = params.request; + } + + public static init(nexus: Nexus, request: Request) { + return new FetchRequestHandler({ + config: nexus.config, + chainRegistry: nexus.chainRegistry, + rpcEndpointPoolFactory: nexus.rpcEndpointPoolFactory, + request, + }); + } + + private async parseJSONRpcRequest() { + // we clean the request to remove any non-required pieces + let payload: unknown; + + try { + payload = await this.request.json(); + } catch (error) { + console.error( + JSON.stringify( + { + request: this.request, + error, + }, + null, + 2 + ) + ); + + return { + type: "invalid-json-request", + request: this.request, + error, + } as const; + } + + const parsedPayload = JsonRPCRequestSchema.safeParse(payload); + + if (!parsedPayload.success) { + console.error(parsedPayload.error); + + return { + type: "invalid-json-rpc-request", + request: this.request, + payload, + error: parsedPayload.error, + } as const; + } + + return { + type: "success", + request: this.request, + data: parsedPayload.data, + } as const; + } + + protected async getContext(): Promise { + const requestUrl = new URL(this.request.url); + const requestPath = requestUrl.pathname; + + const route = matchPath(requestUrl.pathname); + const chain = route + ? this.chainRegistry.getByOptionalParams(route.params) + : undefined; + const pool = chain + ? this.rpcEndpointPoolFactory.fromChain(chain) + : undefined; + + const jsonRPCRequestParseResult = await this.parseJSONRpcRequest(); + + const clientAccessKey = requestUrl.searchParams.get("key") || undefined; + + return new RpcProxyContext({ + pool, + config: this.config, + chain, + path: requestPath, + clientAccessKey, + httpMethod: this.request.method, + jsonRPCRequest: + jsonRPCRequestParseResult.type === "success" + ? jsonRPCRequestParseResult.data + : undefined, + }); + } + + protected handlePreResponse(preResponse: NexusPreResponse) { + if (preResponse.type === "json") { + return Response.json(preResponse.body, { + status: preResponse.status, + }); + } + + console.error("Unsupported response type", preResponse); + + return Response.json( + { + message: "Unsupported response type", + }, + { + status: 500, + } + ); + } +} diff --git a/packages/nexus/src/request-handler/node-request-handler.ts b/packages/nexus/src/request-handler/node-request-handler.ts new file mode 100644 index 0000000..4fc1440 --- /dev/null +++ b/packages/nexus/src/request-handler/node-request-handler.ts @@ -0,0 +1,205 @@ +import type http from "node:http"; +import NodeURL from "node:url"; +import querystring from "node:querystring"; +import type { Config } from "../config"; +import type { ChainRegistry } from "../chain/chain-registry"; +import { matchPath } from "../routes/routes"; +import { JsonRPCRequestSchema } from "../rpc-endpoint/json-rpc-types"; +import type { RpcEndpointPoolFactory } from "../rpc-endpoint/rpc-endpoint-pool-factory"; +import type { Nexus } from "../nexus"; +import { RpcProxyContext } from "./rpc-proxy-context"; +import type { NexusPreResponse } from "./request-handler"; +import { RequestHandler } from "./request-handler"; + +export class NodeRequestHandler extends RequestHandler { + private readonly req: http.IncomingMessage; + private readonly res: http.ServerResponse; + + constructor(params: { + config: Config; + chainRegistry: ChainRegistry; + rpcEndpointPoolFactory: RpcEndpointPoolFactory; + req: http.IncomingMessage; + res: http.ServerResponse; + }) { + super(params); + this.req = params.req; + this.res = params.res; + } + + public static init( + nexus: Nexus, + req: http.IncomingMessage, + res: http.ServerResponse + ) { + return new NodeRequestHandler({ + config: nexus.config, + chainRegistry: nexus.chainRegistry, + rpcEndpointPoolFactory: nexus.rpcEndpointPoolFactory, + req, + res, + }); + } + + protected handlePreResponse(preResponse: NexusPreResponse) { + if (preResponse.type === "json") { + this.res.writeHead(preResponse.status, { + "Content-Type": "application/json", + }); + this.res.end(JSON.stringify(preResponse.body)); + } else { + console.error(preResponse); + const message = `Unsupported response type: ${preResponse.type}`; + const error = new Error(message); + + this.handle500(error, message); + } + } + + public handle500(error: unknown, message?: string) { + console.error(error); + this.res.writeHead(500, { + "Content-Type": "application/json", + }); + this.res.end( + JSON.stringify({ message: message ?? "Internal Server Error" }) + ); + } + + private parseJSONFromNodeHttpRequest() { + // TODO: is any the right choice? + // TODO: test + const body: any[] = []; + let receivedSize = 0; + const maxSize = 1e6; // 1 MB, for example + + return new Promise((resolve, reject) => { + this.req.on("data", (chunk) => { + receivedSize += chunk.length; + + if (receivedSize > maxSize) { + reject(new Error("Payload too large")); + this.req.destroy(); + } else { + body.push(chunk); + } + }); + + this.req.on("end", () => { + try { + const parsedBody: unknown = JSON.parse( + Buffer.concat(body).toString() + ); + + resolve(parsedBody); + } catch (error) { + reject(new Error("Invalid JSON")); + } + }); + + this.req.on("error", (error) => { + reject(error); + }); + }); + } + + private async parseJSONRpcRequest() { + let payload: unknown; + + try { + // TODO: cover the cases where the request is not a json rpc request + // TODO: cover the cases where the request is already parsed as a json object + // from a higher middleware + payload = await this.parseJSONFromNodeHttpRequest(); + } catch (error) { + console.error( + JSON.stringify( + { + req: this.req, + error, + }, + null, + 2 + ) + ); + + return { + type: "invalid-json-request", + req: this.req, + error, + } as const; + } + + try { + const parsedPayload = JsonRPCRequestSchema.safeParse(payload); + + if (!parsedPayload.success) { + console.log("error parsing json rpc request", payload); + console.error(parsedPayload.error); + + return { + type: "invalid-json-rpc-request", + req: this.req, + payload, + error: parsedPayload.error, + } as const; + } + + return { + type: "success", + req: this.req, + data: parsedPayload.data, + } as const; + } catch (error) { + console.error( + JSON.stringify( + { + req: this.req, + error, + }, + null, + 2 + ) + ); + + return { + type: "invalid-json-request", + req: this.req, + error, + } as const; + } + } + + protected async getContext(): Promise { + // TODO: test node http requests + const requestUrl = NodeURL.parse(this.req.url || ""); + const requestPath = requestUrl.pathname; + const { query } = requestUrl; + + const route = requestPath ? matchPath(requestPath) : undefined; + const chain = route + ? this.chainRegistry.getByOptionalParams(route.params) + : undefined; + const pool = chain + ? this.rpcEndpointPoolFactory.fromChain(chain) + : undefined; + + const clientAccessKey = query ? querystring.parse(query).key : undefined; + + const jsonRPCRequestParseResult = await this.parseJSONRpcRequest(); + + return new RpcProxyContext({ + pool, + config: this.config, + httpMethod: this.req.method, + chain, + path: requestPath || "", // TODO: remove this hack, make this more robust + clientAccessKey: + typeof clientAccessKey === "string" ? clientAccessKey : undefined, + jsonRPCRequest: + jsonRPCRequestParseResult.type === "success" + ? jsonRPCRequestParseResult.data + : undefined, + }); + } +} diff --git a/packages/nexus/src/request-handler/request-handler.ts b/packages/nexus/src/request-handler/request-handler.ts index 6061ab7..54d46c0 100644 --- a/packages/nexus/src/request-handler/request-handler.ts +++ b/packages/nexus/src/request-handler/request-handler.ts @@ -1,313 +1,69 @@ -import type * as http from "node:http"; -import NodeURL from "node:url"; -import querystring from "node:querystring"; -import type { Config } from "../config"; -import { matchPath } from "../routes/routes"; -import { RpcEndpointPoolFactory } from "../rpc-endpoint/rpc-endpoint-pool-factory"; import type { ChainRegistry } from "../chain/chain-registry"; -import { defaultChainRegistry } from "../setup/data"; -import { JsonRPCRequestSchema } from "../rpc-endpoint/json-rpc-types"; -import { RpcProxyContext } from "./rpc-proxy-context"; +import type { Config } from "../config"; +import type { RpcEndpointPoolFactory } from "../rpc-endpoint/rpc-endpoint-pool-factory"; +import type { RpcProxyContext } from "./rpc-proxy-context"; -export class RequestHandler { - private readonly endpointFactory: RpcEndpointPoolFactory; - private readonly chainRegistry: ChainRegistry; - private readonly config: Config; +export interface NexusPreResponse { + status: number; + body: unknown; + type: "json" | "text"; +} - constructor(params: { config: Config; chainRegistry?: ChainRegistry }) { +export abstract class RequestHandler { + protected config: Config; + protected chainRegistry: ChainRegistry; + protected rpcEndpointPoolFactory: RpcEndpointPoolFactory; + + constructor(params: { + config: Config; + chainRegistry: ChainRegistry; + rpcEndpointPoolFactory: RpcEndpointPoolFactory; + }) { this.config = params.config; - this.endpointFactory = new RpcEndpointPoolFactory({ - config: this.config, - }); - this.chainRegistry = params.chainRegistry ?? defaultChainRegistry; + this.chainRegistry = params.chainRegistry; + this.rpcEndpointPoolFactory = params.rpcEndpointPoolFactory; } - private parseJSONFromNodeHttpRequest(req: http.IncomingMessage) { - // TODO: is any the right choice? - // TODO: test - const body: any[] = []; - let receivedSize = 0; - const maxSize = 1e6; // 1 MB, for example - - return new Promise((resolve, reject) => { - req.on("data", (chunk) => { - receivedSize += chunk.length; + public async handle(): Promise { + const context = await this.getContext(); + const preResponse = await this.getPreResponseFromContext(context); - if (receivedSize > maxSize) { - reject(new Error("Payload too large")); - req.destroy(); - } else { - body.push(chunk); - } - }); - - req.on("end", () => { - try { - const parsedBody: unknown = JSON.parse( - Buffer.concat(body).toString() - ); - - resolve(parsedBody); - } catch (error) { - reject(new Error("Invalid JSON")); - } - }); - - req.on("error", (error) => { - reject(error); - }); - }); + return this.handlePreResponse(preResponse); } - private async parseJSONRpcRequestFromNodeHttpRequest( - req: http.IncomingMessage - ) { - let payload: unknown; - - try { - // TODO: cover the cases where the request is not a json rpc request - // TODO: cover the cases where the request is already parsed as a json object - // from a higher middleware - payload = await this.parseJSONFromNodeHttpRequest(req); - } catch (error) { - console.error( - JSON.stringify( - { - req, - error, - }, - null, - 2 - ) - ); - - return { - type: "invalid-json-request", - req, - error, - } as const; - } - - try { - console.log("PAYLOAD:", payload); - - const parsedPayload = JsonRPCRequestSchema.safeParse(payload); - - if (!parsedPayload.success) { - console.log("HELLO!"); - console.log("error parsing json rpc request", payload); - console.error(parsedPayload.error); - - return { - type: "invalid-json-rpc-request", - req, - payload, - error: parsedPayload.error, - } as const; - } - - return { - type: "success", - req, - data: parsedPayload.data, - } as const; - } catch (error) { - console.error( - JSON.stringify( - { - req, - error, - }, - null, - 2 - ) - ); - - return { - type: "invalid-json-request", - req, - error, - } as const; - } - } - - private async parseJSONRpcRequestFromFetchRequest(request: Request) { - // we clean the request to remove any non-required pieces - let payload: unknown; - - try { - payload = await request.json(); - } catch (error) { - console.error( - JSON.stringify( - { - request, - error, - }, - null, - 2 - ) - ); + protected async getPreResponseFromContext( + context: RpcProxyContext + ): Promise { + if (context.httpMethod === "GET") { + const status = await context.getStatus(); return { - type: "invalid-json-request", - request, - error, - } as const; - } - - const parsedPayload = JsonRPCRequestSchema.safeParse(payload); - - if (!parsedPayload.success) { - console.error(parsedPayload.error); + status: status.code, + body: status, + type: "json", + }; + } else if (context.httpMethod === "POST") { + const result = await context.relay(); return { - type: "invalid-json-rpc-request", - request, - payload, - error: parsedPayload.error, - } as const; + status: result.status, + body: result.body, + type: "json", + }; } return { - type: "success", - request, - data: parsedPayload.data, - } as const; - } - - private async buildContextFromFetchRequest( - request: Request - ): Promise { - const requestUrl = new URL(request.url); - const requestPath = requestUrl.pathname; - - const route = matchPath(requestUrl.pathname); - const chain = route - ? this.chainRegistry.getByOptionalParams(route.params) - : undefined; - const pool = chain ? this.endpointFactory.fromChain(chain) : undefined; - - const jsonRPCRequestParseResult = - await this.parseJSONRpcRequestFromFetchRequest(request); - - const clientAccessKey = requestUrl.searchParams.get("key") || undefined; - - return new RpcProxyContext({ - pool, - config: this.config, - chain, - path: requestPath, - clientAccessKey, - jsonRPCRequest: - jsonRPCRequestParseResult.type === "success" - ? jsonRPCRequestParseResult.data - : undefined, - }); - } - - private async buildContextFromNodeHttpRequest( - req: http.IncomingMessage - ): Promise { - // TODO: test node http requests - console.log("URL:", req.url); - const requestUrl = NodeURL.parse(req.url || ""); - const requestPath = requestUrl.pathname; - const { query } = requestUrl; - - const route = requestPath ? matchPath(requestPath) : undefined; - const chain = route - ? this.chainRegistry.getByOptionalParams(route.params) - : undefined; - const pool = chain ? this.endpointFactory.fromChain(chain) : undefined; - - console.log({ - chain, - route, - pool, - }); - - const clientAccessKey = query ? querystring.parse(query).key : undefined; - - const jsonRPCRequestParseResult = - await this.parseJSONRpcRequestFromNodeHttpRequest(req); - - return new RpcProxyContext({ - pool, - config: this.config, - chain, - path: requestPath || "", // TODO: remove this hack, make this more robust - clientAccessKey: - typeof clientAccessKey === "string" ? clientAccessKey : undefined, - jsonRPCRequest: - jsonRPCRequestParseResult.type === "success" - ? jsonRPCRequestParseResult.data - : undefined, - }); - } - - private async handleStatus(context: RpcProxyContext) { - const status = await context.getStatus(); - - return { - status: status.code, - body: status, - }; - } - - public async handleFetch(request: Request): Promise { - const context = await this.buildContextFromFetchRequest(request); - - if (request.method === "GET") { - const responseRaw = await this.handleStatus(context); - - return Response.json(responseRaw.body, { status: responseRaw.status }); - } else if (request.method === "POST") { - const result = await context.relay(); - - return Response.json(result.body, { status: result.status }); - } - - return Response.json( - { + status: 404, + body: { message: "Not Found", }, - { status: 404 } - ); + type: "json", + }; } - public async handleNodeHttp( - req: http.IncomingMessage, - res: http.ServerResponse - ) { - const context = await this.buildContextFromNodeHttpRequest(req); - - if (req.method === "GET") { - const status = await context.getStatus(); - - res.writeHead(status.code, { - "Content-Type": "application/json", - }); - - res.end(JSON.stringify(status)); - } else if (req.method === "POST") { - const result = await context.relay(); + protected abstract getContext(): Promise; - res.writeHead(result.status, { - "Content-Type": "application/json", - }); - - res.end(JSON.stringify(result.body)); - } else { - res.writeHead(404, { - "Content-Type": "application/json", - }); - - res.end( - JSON.stringify({ - message: "Not Found", - }) - ); - } - } + protected abstract handlePreResponse( + response: NexusPreResponse + ): ResponseReturnType; } diff --git a/packages/nexus/src/request-handler/rpc-proxy-context.ts b/packages/nexus/src/request-handler/rpc-proxy-context.ts index 48e3cf1..10ef178 100644 --- a/packages/nexus/src/request-handler/rpc-proxy-context.ts +++ b/packages/nexus/src/request-handler/rpc-proxy-context.ts @@ -29,7 +29,7 @@ type Status = SuccessStatus | ErrorStatus; export class RpcProxyContext { public readonly chain?: Chain; public readonly pool?: RpcEndpointPool; - + public readonly httpMethod?: string; public readonly jsonRPCRequest?: JsonRPCRequest; public relayResult?: Awaited>; private readonly config: Config; @@ -44,6 +44,7 @@ export class RpcProxyContext { jsonRPCRequest?: JsonRPCRequest; path: string; clientAccessKey?: string; + httpMethod?: string; }) { this.chain = params.chain; this.config = params.config; @@ -51,6 +52,7 @@ export class RpcProxyContext { this.pool = params.pool; this.path = params.path; this.clientAccessKey = params.clientAccessKey; + this.httpMethod = params.httpMethod; } private buildStatus(params: { diff --git a/packages/nexus/src/rpc-endpoint/rpc-endpoint-pool-factory.ts b/packages/nexus/src/rpc-endpoint/rpc-endpoint-pool-factory.ts index 6f8dfce..51007e0 100644 --- a/packages/nexus/src/rpc-endpoint/rpc-endpoint-pool-factory.ts +++ b/packages/nexus/src/rpc-endpoint/rpc-endpoint-pool-factory.ts @@ -1,5 +1,4 @@ import type { Config } from "../config"; -import { defaultServiceProviderRegistry } from "../setup/data"; import type { ChainRegistry } from "../chain/chain-registry"; import type { Chain } from "../chain/chain"; import type { ServiceProviderRegistry } from "../service-provider/service-provider-registry"; @@ -11,12 +10,11 @@ export class RpcEndpointPoolFactory { private readonly serviceProviderRegistry: ServiceProviderRegistry; constructor(params: { config: Config; - chainRegistry?: ChainRegistry; - serviceProviderRegistry?: ServiceProviderRegistry; + chainRegistry: ChainRegistry; + serviceProviderRegistry: ServiceProviderRegistry; }) { this.config = params.config; - this.serviceProviderRegistry = - params.serviceProviderRegistry ?? defaultServiceProviderRegistry; + this.serviceProviderRegistry = params.serviceProviderRegistry; } public fromChain(chain: Chain): RpcEndpointPool { From 9c7f27d97567d159aaf2d35b868ec3254c3347ac Mon Sep 17 00:00:00 2001 From: Kerem Kazan Date: Sun, 12 Nov 2023 12:45:41 -0500 Subject: [PATCH 15/41] package.json categories -> keywords --- packages/nexus/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/nexus/package.json b/packages/nexus/package.json index 3d74112..fcaedbe 100644 --- a/packages/nexus/package.json +++ b/packages/nexus/package.json @@ -4,7 +4,7 @@ "description": "A simple TypeScript proxy server for any Ethereum JSON RPC compliant blockchain", "private": false, "access": "public", - "categories": [ + "keywords": [ "blockchain", "ethereum", "json-rpc", From 6fd0fe1a3113aa49cfc9a6d2f5d290275f86796e Mon Sep 17 00:00:00 2001 From: Kerem Kazan Date: Sun, 12 Nov 2023 13:58:26 -0500 Subject: [PATCH 16/41] nexus now pulls all configs together --- packages/nexus/src/nexus/nexus.ts | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/packages/nexus/src/nexus/nexus.ts b/packages/nexus/src/nexus/nexus.ts index d797bef..0bcbbf4 100644 --- a/packages/nexus/src/nexus/nexus.ts +++ b/packages/nexus/src/nexus/nexus.ts @@ -1,17 +1,33 @@ +import type { ChainRegistry } from "../chain/chain-registry"; import { Config } from "../config"; import type { ConfigConstructorParams } from "../config"; -import { RequestHandler } from "../request-handler/request-handler"; +import { RpcEndpointPoolFactory } from "../rpc-endpoint/rpc-endpoint-pool-factory"; +import type { ServiceProviderRegistry } from "../service-provider/service-provider-registry"; +import { + defaultChainRegistry, + defaultServiceProviderRegistry, +} from "../setup/data"; export class Nexus { - private readonly config: Config; + public readonly config: Config; + public readonly serviceProviderRegistry: ServiceProviderRegistry; + public readonly chainRegistry: ChainRegistry; + public readonly rpcEndpointPoolFactory: RpcEndpointPoolFactory; - public readonly requestHandler: RequestHandler; - - constructor(params: ConfigConstructorParams = {}) { + constructor( + params: ConfigConstructorParams & { + chainRegistry?: ChainRegistry; + serviceProviderRegistry?: ServiceProviderRegistry; + } = {} + ) { this.config = new Config(params); - this.requestHandler = new RequestHandler({ + this.chainRegistry = params.chainRegistry ?? defaultChainRegistry; + this.serviceProviderRegistry = + params.serviceProviderRegistry ?? defaultServiceProviderRegistry; + this.rpcEndpointPoolFactory = new RpcEndpointPoolFactory({ + chainRegistry: this.chainRegistry, config: this.config, - // TODO: make the chain registry part of the config + serviceProviderRegistry: this.serviceProviderRegistry, }); } } From d07a221b03b7306b109a88187a66e24e8ff93cb8 Mon Sep 17 00:00:00 2001 From: Kerem Kazan Date: Sun, 12 Nov 2023 16:15:33 -0500 Subject: [PATCH 17/41] docs removed from pnpm-lock --- pnpm-lock.yaml | 2 -- 1 file changed, 2 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5ac28da..ea2d613 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -33,8 +33,6 @@ importers: specifier: 5.2.2 version: 5.2.2 - docs: {} - examples/nexus-worker: dependencies: '@whatsgood/nexus': From de58890ea3076682828700d80f2971e79d0ae327 Mon Sep 17 00:00:00 2001 From: Kerem Kazan Date: Sun, 12 Nov 2023 16:23:50 -0500 Subject: [PATCH 18/41] node and fetch handlers are exported separately --- examples/nexus-worker/src/index.ts | 4 ++-- examples/nexus-worker/tsconfig.json | 6 +++--- packages/nexus/package.json | 14 ++++++++++++-- packages/nexus/src/entrypoints/fetch.ts | 6 ++++++ packages/nexus/src/entrypoints/node.ts | 6 ++++++ packages/nexus/src/index.ts | 4 ---- ...uest-handler.ts => abstract-request-handler.ts} | 2 +- .../src/request-handler/fetch-request-handler.ts | 6 +++--- .../src/request-handler/node-request-handler.ts | 6 +++--- packages/nexus/tsup.config.ts | 6 ++++-- 10 files changed, 40 insertions(+), 20 deletions(-) create mode 100644 packages/nexus/src/entrypoints/fetch.ts create mode 100644 packages/nexus/src/entrypoints/node.ts rename packages/nexus/src/request-handler/{request-handler.ts => abstract-request-handler.ts} (96%) diff --git a/examples/nexus-worker/src/index.ts b/examples/nexus-worker/src/index.ts index d22de50..4e26de7 100644 --- a/examples/nexus-worker/src/index.ts +++ b/examples/nexus-worker/src/index.ts @@ -1,4 +1,4 @@ -import { Nexus } from "@whatsgood/nexus"; +import { Nexus, RequestHandler } from "@whatsgood/nexus/fetch"; // TODO: add config alerts to indicate that the key access is incomplete // TODO: add onboarding & UX. (setup admin access, login, etc) @@ -12,6 +12,6 @@ export default { const nexus = new Nexus({ env, }); - return nexus.requestHandler.handleFetch(request); + return RequestHandler.init(nexus, request).handle(); }, }; diff --git a/examples/nexus-worker/tsconfig.json b/examples/nexus-worker/tsconfig.json index 2468473..a73d933 100644 --- a/examples/nexus-worker/tsconfig.json +++ b/examples/nexus-worker/tsconfig.json @@ -5,11 +5,11 @@ "compilerOptions": { "target": "es2021", "lib": ["es2021"], - "module": "es2022", - "moduleResolution": "node", "types": ["@cloudflare/workers-types"], "resolveJsonModule": true, "allowJs": false, - "allowSyntheticDefaultImports": true + "allowSyntheticDefaultImports": true, + "moduleResolution": "Node16", + "module": "Node16" } } diff --git a/packages/nexus/package.json b/packages/nexus/package.json index fcaedbe..1dcef21 100644 --- a/packages/nexus/package.json +++ b/packages/nexus/package.json @@ -19,9 +19,19 @@ "type": "git", "url": "https://github.com/whats-good/nexus" }, + "exports": { + "./fetch": { + "require": "./dist/fetch.js", + "import": "./dist/fetch.js", + "types": "./dist/fetch.d.ts" + }, + "./node": { + "require": "./dist/node.js", + "import": "./dist/node.js", + "types": "./dist/node.d.ts" + } + }, "license": "MIT", - "types": "./dist/index.d.ts", - "main": "./dist/index.js", "scripts": { "dev": "echo 'Add dev script here'", "build": "tsup", diff --git a/packages/nexus/src/entrypoints/fetch.ts b/packages/nexus/src/entrypoints/fetch.ts new file mode 100644 index 0000000..ffb6ea5 --- /dev/null +++ b/packages/nexus/src/entrypoints/fetch.ts @@ -0,0 +1,6 @@ +// TODO: how can i let the user specify the load balancing weights per service provider or even per custom provider url? +// TODO: how do i build fallback logic? for example, `if ankr fails, fallback to infura`? + +export * from "../config"; +export * from "../nexus"; +export { FetchRequestHandler as RequestHandler } from "../request-handler/fetch-request-handler"; diff --git a/packages/nexus/src/entrypoints/node.ts b/packages/nexus/src/entrypoints/node.ts new file mode 100644 index 0000000..764164b --- /dev/null +++ b/packages/nexus/src/entrypoints/node.ts @@ -0,0 +1,6 @@ +// TODO: how can i let the user specify the load balancing weights per service provider or even per custom provider url? +// TODO: how do i build fallback logic? for example, `if ankr fails, fallback to infura`? + +export * from "../config"; +export * from "../nexus"; +export { NodeRequestHandler as RequestHandler } from "../request-handler/node-request-handler"; diff --git a/packages/nexus/src/index.ts b/packages/nexus/src/index.ts index 117a123..cbaa980 100644 --- a/packages/nexus/src/index.ts +++ b/packages/nexus/src/index.ts @@ -1,5 +1 @@ -// TODO: how can i let the user specify the load balancing weights per service provider or even per custom provider url? -// TODO: how do i build fallback logic? for example, `if ankr fails, fallback to infura`? - -export * from "./config"; export * from "./nexus"; diff --git a/packages/nexus/src/request-handler/request-handler.ts b/packages/nexus/src/request-handler/abstract-request-handler.ts similarity index 96% rename from packages/nexus/src/request-handler/request-handler.ts rename to packages/nexus/src/request-handler/abstract-request-handler.ts index 54d46c0..19c8872 100644 --- a/packages/nexus/src/request-handler/request-handler.ts +++ b/packages/nexus/src/request-handler/abstract-request-handler.ts @@ -9,7 +9,7 @@ export interface NexusPreResponse { type: "json" | "text"; } -export abstract class RequestHandler { +export abstract class AbstractRequestHandler { protected config: Config; protected chainRegistry: ChainRegistry; protected rpcEndpointPoolFactory: RpcEndpointPoolFactory; diff --git a/packages/nexus/src/request-handler/fetch-request-handler.ts b/packages/nexus/src/request-handler/fetch-request-handler.ts index cb1cc4e..fa1460c 100644 --- a/packages/nexus/src/request-handler/fetch-request-handler.ts +++ b/packages/nexus/src/request-handler/fetch-request-handler.ts @@ -5,10 +5,10 @@ import { JsonRPCRequestSchema } from "../rpc-endpoint/json-rpc-types"; import type { RpcEndpointPoolFactory } from "../rpc-endpoint/rpc-endpoint-pool-factory"; import type { Nexus } from "../nexus"; import { RpcProxyContext } from "./rpc-proxy-context"; -import type { NexusPreResponse } from "./request-handler"; -import { RequestHandler } from "./request-handler"; +import type { NexusPreResponse } from "./abstract-request-handler"; +import { AbstractRequestHandler } from "./abstract-request-handler"; -export class FetchRequestHandler extends RequestHandler { +export class FetchRequestHandler extends AbstractRequestHandler { private readonly request: Request; constructor(params: { diff --git a/packages/nexus/src/request-handler/node-request-handler.ts b/packages/nexus/src/request-handler/node-request-handler.ts index 4fc1440..649506a 100644 --- a/packages/nexus/src/request-handler/node-request-handler.ts +++ b/packages/nexus/src/request-handler/node-request-handler.ts @@ -8,10 +8,10 @@ import { JsonRPCRequestSchema } from "../rpc-endpoint/json-rpc-types"; import type { RpcEndpointPoolFactory } from "../rpc-endpoint/rpc-endpoint-pool-factory"; import type { Nexus } from "../nexus"; import { RpcProxyContext } from "./rpc-proxy-context"; -import type { NexusPreResponse } from "./request-handler"; -import { RequestHandler } from "./request-handler"; +import type { NexusPreResponse } from "./abstract-request-handler"; +import { AbstractRequestHandler } from "./abstract-request-handler"; -export class NodeRequestHandler extends RequestHandler { +export class NodeRequestHandler extends AbstractRequestHandler { private readonly req: http.IncomingMessage; private readonly res: http.ServerResponse; diff --git a/packages/nexus/tsup.config.ts b/packages/nexus/tsup.config.ts index 679cb19..88f5370 100644 --- a/packages/nexus/tsup.config.ts +++ b/packages/nexus/tsup.config.ts @@ -3,11 +3,13 @@ import { defineConfig } from "tsup"; export default defineConfig((options: Options) => ({ treeshake: true, - entry: ["./src/**/!(*.test).ts"], + entry: ["./src/entrypoints/fetch.ts", "./src/entrypoints/node.ts"], format: ["cjs"], dts: true, - minify: false, + minify: true, clean: true, sourcemap: true, + splitting: true, + outDir: "dist", ...options, })); From cb02afa21461f98eae5126710ea9d1ab6eba7882 Mon Sep 17 00:00:00 2001 From: Kerem Kazan Date: Sun, 12 Nov 2023 16:30:47 -0500 Subject: [PATCH 19/41] moved source files into /lib folder --- docs/recipes/cloudflare-worker.mdx | 2 ++ packages/nexus/src/{entrypoints => }/fetch.ts | 6 +++--- packages/nexus/src/index.ts | 1 - packages/nexus/src/{ => lib}/chain/chain-registry.ts | 0 packages/nexus/src/{ => lib}/chain/chain.ts | 0 packages/nexus/src/{ => lib}/config.ts | 0 packages/nexus/src/{ => lib}/nexus/index.ts | 0 packages/nexus/src/{ => lib}/nexus/nexus.ts | 0 .../{ => lib}/request-handler/abstract-request-handler.ts | 0 .../{ => lib}/request-handler/fetch-request-handler.ts | 0 .../src/{ => lib}/request-handler/node-request-handler.ts | 0 .../request-handler/request-handler.relay.test.ts | 8 ++++---- .../request-handler/request-handler.status.test.ts | 6 +++--- .../src/{ => lib}/request-handler/rpc-proxy-context.ts | 0 packages/nexus/src/{ => lib}/routes/routes.test.ts | 0 packages/nexus/src/{ => lib}/routes/routes.ts | 0 .../nexus/src/{ => lib}/rpc-endpoint/json-rpc-types.ts | 0 .../rpc-endpoint/rpc-endpoint-pool-factory.test.ts | 0 .../{ => lib}/rpc-endpoint/rpc-endpoint-pool-factory.ts | 0 .../nexus/src/{ => lib}/rpc-endpoint/rpc-endpoint-pool.ts | 0 packages/nexus/src/{ => lib}/rpc-endpoint/rpc-endpoint.ts | 0 .../service-provider/service-provider-registry.ts | 0 .../src/{ => lib}/service-provider/service-provider.ts | 0 packages/nexus/src/{ => lib}/setup/data.ts | 0 packages/nexus/src/{ => lib}/utils.ts | 0 packages/nexus/src/{entrypoints => }/node.ts | 6 +++--- packages/nexus/tsup.config.ts | 2 +- 27 files changed, 16 insertions(+), 15 deletions(-) rename packages/nexus/src/{entrypoints => }/fetch.ts (56%) delete mode 100644 packages/nexus/src/index.ts rename packages/nexus/src/{ => lib}/chain/chain-registry.ts (100%) rename packages/nexus/src/{ => lib}/chain/chain.ts (100%) rename packages/nexus/src/{ => lib}/config.ts (100%) rename packages/nexus/src/{ => lib}/nexus/index.ts (100%) rename packages/nexus/src/{ => lib}/nexus/nexus.ts (100%) rename packages/nexus/src/{ => lib}/request-handler/abstract-request-handler.ts (100%) rename packages/nexus/src/{ => lib}/request-handler/fetch-request-handler.ts (100%) rename packages/nexus/src/{ => lib}/request-handler/node-request-handler.ts (100%) rename packages/nexus/src/{ => lib}/request-handler/request-handler.relay.test.ts (94%) rename packages/nexus/src/{ => lib}/request-handler/request-handler.status.test.ts (96%) rename packages/nexus/src/{ => lib}/request-handler/rpc-proxy-context.ts (100%) rename packages/nexus/src/{ => lib}/routes/routes.test.ts (100%) rename packages/nexus/src/{ => lib}/routes/routes.ts (100%) rename packages/nexus/src/{ => lib}/rpc-endpoint/json-rpc-types.ts (100%) rename packages/nexus/src/{ => lib}/rpc-endpoint/rpc-endpoint-pool-factory.test.ts (100%) rename packages/nexus/src/{ => lib}/rpc-endpoint/rpc-endpoint-pool-factory.ts (100%) rename packages/nexus/src/{ => lib}/rpc-endpoint/rpc-endpoint-pool.ts (100%) rename packages/nexus/src/{ => lib}/rpc-endpoint/rpc-endpoint.ts (100%) rename packages/nexus/src/{ => lib}/service-provider/service-provider-registry.ts (100%) rename packages/nexus/src/{ => lib}/service-provider/service-provider.ts (100%) rename packages/nexus/src/{ => lib}/setup/data.ts (100%) rename packages/nexus/src/{ => lib}/utils.ts (100%) rename packages/nexus/src/{entrypoints => }/node.ts (57%) diff --git a/docs/recipes/cloudflare-worker.mdx b/docs/recipes/cloudflare-worker.mdx index 8dfb064..54eecb4 100644 --- a/docs/recipes/cloudflare-worker.mdx +++ b/docs/recipes/cloudflare-worker.mdx @@ -64,6 +64,8 @@ You can now `cd` into your new project directory and open it in your favorite ed } ``` +{/* TODO: add the new tsconfig to make the new exports work */} + ```toml wrangler.toml name = "nexus-rpc-worker" main = "src/index.ts" diff --git a/packages/nexus/src/entrypoints/fetch.ts b/packages/nexus/src/fetch.ts similarity index 56% rename from packages/nexus/src/entrypoints/fetch.ts rename to packages/nexus/src/fetch.ts index ffb6ea5..4670f8c 100644 --- a/packages/nexus/src/entrypoints/fetch.ts +++ b/packages/nexus/src/fetch.ts @@ -1,6 +1,6 @@ // TODO: how can i let the user specify the load balancing weights per service provider or even per custom provider url? // TODO: how do i build fallback logic? for example, `if ankr fails, fallback to infura`? -export * from "../config"; -export * from "../nexus"; -export { FetchRequestHandler as RequestHandler } from "../request-handler/fetch-request-handler"; +export * from "./lib/config"; +export * from "./lib/nexus"; +export { FetchRequestHandler as RequestHandler } from "./lib/request-handler/fetch-request-handler"; diff --git a/packages/nexus/src/index.ts b/packages/nexus/src/index.ts deleted file mode 100644 index cbaa980..0000000 --- a/packages/nexus/src/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./nexus"; diff --git a/packages/nexus/src/chain/chain-registry.ts b/packages/nexus/src/lib/chain/chain-registry.ts similarity index 100% rename from packages/nexus/src/chain/chain-registry.ts rename to packages/nexus/src/lib/chain/chain-registry.ts diff --git a/packages/nexus/src/chain/chain.ts b/packages/nexus/src/lib/chain/chain.ts similarity index 100% rename from packages/nexus/src/chain/chain.ts rename to packages/nexus/src/lib/chain/chain.ts diff --git a/packages/nexus/src/config.ts b/packages/nexus/src/lib/config.ts similarity index 100% rename from packages/nexus/src/config.ts rename to packages/nexus/src/lib/config.ts diff --git a/packages/nexus/src/nexus/index.ts b/packages/nexus/src/lib/nexus/index.ts similarity index 100% rename from packages/nexus/src/nexus/index.ts rename to packages/nexus/src/lib/nexus/index.ts diff --git a/packages/nexus/src/nexus/nexus.ts b/packages/nexus/src/lib/nexus/nexus.ts similarity index 100% rename from packages/nexus/src/nexus/nexus.ts rename to packages/nexus/src/lib/nexus/nexus.ts diff --git a/packages/nexus/src/request-handler/abstract-request-handler.ts b/packages/nexus/src/lib/request-handler/abstract-request-handler.ts similarity index 100% rename from packages/nexus/src/request-handler/abstract-request-handler.ts rename to packages/nexus/src/lib/request-handler/abstract-request-handler.ts diff --git a/packages/nexus/src/request-handler/fetch-request-handler.ts b/packages/nexus/src/lib/request-handler/fetch-request-handler.ts similarity index 100% rename from packages/nexus/src/request-handler/fetch-request-handler.ts rename to packages/nexus/src/lib/request-handler/fetch-request-handler.ts diff --git a/packages/nexus/src/request-handler/node-request-handler.ts b/packages/nexus/src/lib/request-handler/node-request-handler.ts similarity index 100% rename from packages/nexus/src/request-handler/node-request-handler.ts rename to packages/nexus/src/lib/request-handler/node-request-handler.ts diff --git a/packages/nexus/src/request-handler/request-handler.relay.test.ts b/packages/nexus/src/lib/request-handler/request-handler.relay.test.ts similarity index 94% rename from packages/nexus/src/request-handler/request-handler.relay.test.ts rename to packages/nexus/src/lib/request-handler/request-handler.relay.test.ts index 4231f70..fcfbeab 100644 --- a/packages/nexus/src/request-handler/request-handler.relay.test.ts +++ b/packages/nexus/src/lib/request-handler/request-handler.relay.test.ts @@ -1,9 +1,9 @@ import { afterAll, afterEach, beforeAll, describe, expect, it } from "vitest"; import { setupServer } from "msw/node"; import { Config } from "../config"; -import { handlers } from "../../tests/mock-server-handlers"; -import { retry } from "../../tests/utils"; -import { RequestHandler } from "./request-handler"; +import { handlers } from "../../../tests/mock-server-handlers"; +import { retry } from "../../../tests/utils"; +import { AbstractRequestHandler } from "./abstract-request-handler"; const sharedConfig = { globalAccessKey: "some-key", @@ -31,7 +31,7 @@ const configWithNoRecovery = new Config({ }); const blockNumberRequestHelper = (config: Config) => { - const requestHandler = new RequestHandler({ config }); + const requestHandler = new AbstractRequestHandler({ config }); const request = new Request( "https://my-test-rpc-provider.com/eth/mainnet?key=some-key", { diff --git a/packages/nexus/src/request-handler/request-handler.status.test.ts b/packages/nexus/src/lib/request-handler/request-handler.status.test.ts similarity index 96% rename from packages/nexus/src/request-handler/request-handler.status.test.ts rename to packages/nexus/src/lib/request-handler/request-handler.status.test.ts index 7279f62..429f406 100644 --- a/packages/nexus/src/request-handler/request-handler.status.test.ts +++ b/packages/nexus/src/lib/request-handler/request-handler.status.test.ts @@ -1,11 +1,11 @@ import { afterAll, afterEach, beforeAll, describe, expect, it } from "vitest"; import { setupServer } from "msw/node"; import { Config } from "../config"; -import { handlers } from "../../tests/mock-server-handlers"; -import { RequestHandler } from "./request-handler"; +import { handlers } from "../../../tests/mock-server-handlers"; +import { AbstractRequestHandler } from "./abstract-request-handler"; export const requestHelper = async (endpoint: string, config: Config) => { - const requestHandler = new RequestHandler({ config }); + const requestHandler = new AbstractRequestHandler({ config }); const request = new Request(`https://my-test-rpc-provider.com${endpoint}`, { method: "GET", }); diff --git a/packages/nexus/src/request-handler/rpc-proxy-context.ts b/packages/nexus/src/lib/request-handler/rpc-proxy-context.ts similarity index 100% rename from packages/nexus/src/request-handler/rpc-proxy-context.ts rename to packages/nexus/src/lib/request-handler/rpc-proxy-context.ts diff --git a/packages/nexus/src/routes/routes.test.ts b/packages/nexus/src/lib/routes/routes.test.ts similarity index 100% rename from packages/nexus/src/routes/routes.test.ts rename to packages/nexus/src/lib/routes/routes.test.ts diff --git a/packages/nexus/src/routes/routes.ts b/packages/nexus/src/lib/routes/routes.ts similarity index 100% rename from packages/nexus/src/routes/routes.ts rename to packages/nexus/src/lib/routes/routes.ts diff --git a/packages/nexus/src/rpc-endpoint/json-rpc-types.ts b/packages/nexus/src/lib/rpc-endpoint/json-rpc-types.ts similarity index 100% rename from packages/nexus/src/rpc-endpoint/json-rpc-types.ts rename to packages/nexus/src/lib/rpc-endpoint/json-rpc-types.ts diff --git a/packages/nexus/src/rpc-endpoint/rpc-endpoint-pool-factory.test.ts b/packages/nexus/src/lib/rpc-endpoint/rpc-endpoint-pool-factory.test.ts similarity index 100% rename from packages/nexus/src/rpc-endpoint/rpc-endpoint-pool-factory.test.ts rename to packages/nexus/src/lib/rpc-endpoint/rpc-endpoint-pool-factory.test.ts diff --git a/packages/nexus/src/rpc-endpoint/rpc-endpoint-pool-factory.ts b/packages/nexus/src/lib/rpc-endpoint/rpc-endpoint-pool-factory.ts similarity index 100% rename from packages/nexus/src/rpc-endpoint/rpc-endpoint-pool-factory.ts rename to packages/nexus/src/lib/rpc-endpoint/rpc-endpoint-pool-factory.ts diff --git a/packages/nexus/src/rpc-endpoint/rpc-endpoint-pool.ts b/packages/nexus/src/lib/rpc-endpoint/rpc-endpoint-pool.ts similarity index 100% rename from packages/nexus/src/rpc-endpoint/rpc-endpoint-pool.ts rename to packages/nexus/src/lib/rpc-endpoint/rpc-endpoint-pool.ts diff --git a/packages/nexus/src/rpc-endpoint/rpc-endpoint.ts b/packages/nexus/src/lib/rpc-endpoint/rpc-endpoint.ts similarity index 100% rename from packages/nexus/src/rpc-endpoint/rpc-endpoint.ts rename to packages/nexus/src/lib/rpc-endpoint/rpc-endpoint.ts diff --git a/packages/nexus/src/service-provider/service-provider-registry.ts b/packages/nexus/src/lib/service-provider/service-provider-registry.ts similarity index 100% rename from packages/nexus/src/service-provider/service-provider-registry.ts rename to packages/nexus/src/lib/service-provider/service-provider-registry.ts diff --git a/packages/nexus/src/service-provider/service-provider.ts b/packages/nexus/src/lib/service-provider/service-provider.ts similarity index 100% rename from packages/nexus/src/service-provider/service-provider.ts rename to packages/nexus/src/lib/service-provider/service-provider.ts diff --git a/packages/nexus/src/setup/data.ts b/packages/nexus/src/lib/setup/data.ts similarity index 100% rename from packages/nexus/src/setup/data.ts rename to packages/nexus/src/lib/setup/data.ts diff --git a/packages/nexus/src/utils.ts b/packages/nexus/src/lib/utils.ts similarity index 100% rename from packages/nexus/src/utils.ts rename to packages/nexus/src/lib/utils.ts diff --git a/packages/nexus/src/entrypoints/node.ts b/packages/nexus/src/node.ts similarity index 57% rename from packages/nexus/src/entrypoints/node.ts rename to packages/nexus/src/node.ts index 764164b..802362e 100644 --- a/packages/nexus/src/entrypoints/node.ts +++ b/packages/nexus/src/node.ts @@ -1,6 +1,6 @@ // TODO: how can i let the user specify the load balancing weights per service provider or even per custom provider url? // TODO: how do i build fallback logic? for example, `if ankr fails, fallback to infura`? -export * from "../config"; -export * from "../nexus"; -export { NodeRequestHandler as RequestHandler } from "../request-handler/node-request-handler"; +export * from "./lib/config"; +export * from "./lib/nexus"; +export { NodeRequestHandler as RequestHandler } from "./lib/request-handler/node-request-handler"; diff --git a/packages/nexus/tsup.config.ts b/packages/nexus/tsup.config.ts index 88f5370..25736c4 100644 --- a/packages/nexus/tsup.config.ts +++ b/packages/nexus/tsup.config.ts @@ -3,7 +3,7 @@ import { defineConfig } from "tsup"; export default defineConfig((options: Options) => ({ treeshake: true, - entry: ["./src/entrypoints/fetch.ts", "./src/entrypoints/node.ts"], + entry: ["./src/fetch.ts", "./src/node.ts"], format: ["cjs"], dts: true, minify: true, From 9b056bf136c44155465af7ea8b9a8862c9354998 Mon Sep 17 00:00:00 2001 From: Kerem Kazan Date: Sun, 12 Nov 2023 16:59:21 -0500 Subject: [PATCH 20/41] tests fixed --- packages/nexus/package.json | 17 ++++++++----- .../nexus/src/{node.ts => fetch/index.ts} | 4 +--- .../fetch-request-handler.relay.test.ts} | 24 +++++++++++++++---- .../fetch-request-handler.status.test.ts} | 24 +++++++++++++++---- .../nexus/src/fetch/request-handler/index.ts | 1 + .../request-handler/request-handler.ts} | 24 +++++++++---------- packages/nexus/src/lib/index.ts | 2 ++ packages/nexus/src/lib/routes/index.ts | 1 + .../rpc-endpoint-pool-factory.test.ts | 2 ++ .../nexus/src/{fetch.ts => node/index.ts} | 4 +--- .../nexus/src/node/request-handler/index.ts | 1 + .../request-handler/request-handler.ts} | 24 +++++++++---------- packages/nexus/tsup.config.ts | 2 +- 13 files changed, 83 insertions(+), 47 deletions(-) rename packages/nexus/src/{node.ts => fetch/index.ts} (57%) rename packages/nexus/src/{lib/request-handler/request-handler.relay.test.ts => fetch/request-handler/fetch-request-handler.relay.test.ts} (86%) rename packages/nexus/src/{lib/request-handler/request-handler.status.test.ts => fetch/request-handler/fetch-request-handler.status.test.ts} (88%) create mode 100644 packages/nexus/src/fetch/request-handler/index.ts rename packages/nexus/src/{lib/request-handler/fetch-request-handler.ts => fetch/request-handler/request-handler.ts} (78%) create mode 100644 packages/nexus/src/lib/index.ts create mode 100644 packages/nexus/src/lib/routes/index.ts rename packages/nexus/src/{fetch.ts => node/index.ts} (56%) create mode 100644 packages/nexus/src/node/request-handler/index.ts rename packages/nexus/src/{lib/request-handler/node-request-handler.ts => node/request-handler/request-handler.ts} (86%) diff --git a/packages/nexus/package.json b/packages/nexus/package.json index 1dcef21..7129dac 100644 --- a/packages/nexus/package.json +++ b/packages/nexus/package.json @@ -21,14 +21,19 @@ }, "exports": { "./fetch": { - "require": "./dist/fetch.js", - "import": "./dist/fetch.js", - "types": "./dist/fetch.d.ts" + "require": "./dist/fetch/index.js", + "import": "./dist/fetch/index.js", + "types": "./dist/fetch/index.d.ts" }, "./node": { - "require": "./dist/node.js", - "import": "./dist/node.js", - "types": "./dist/node.d.ts" + "require": "./dist/node/index.js", + "import": "./dist/node/index.js", + "types": "./dist/node/index.d.ts" + }, + ".": { + "require": "./dist/lib/index.js", + "import": "./dist/lib/index.js", + "types": "./dist/lib/index.d.ts" } }, "license": "MIT", diff --git a/packages/nexus/src/node.ts b/packages/nexus/src/fetch/index.ts similarity index 57% rename from packages/nexus/src/node.ts rename to packages/nexus/src/fetch/index.ts index 802362e..7ca401b 100644 --- a/packages/nexus/src/node.ts +++ b/packages/nexus/src/fetch/index.ts @@ -1,6 +1,4 @@ // TODO: how can i let the user specify the load balancing weights per service provider or even per custom provider url? // TODO: how do i build fallback logic? for example, `if ankr fails, fallback to infura`? -export * from "./lib/config"; -export * from "./lib/nexus"; -export { NodeRequestHandler as RequestHandler } from "./lib/request-handler/node-request-handler"; +export { RequestHandler } from "./request-handler"; diff --git a/packages/nexus/src/lib/request-handler/request-handler.relay.test.ts b/packages/nexus/src/fetch/request-handler/fetch-request-handler.relay.test.ts similarity index 86% rename from packages/nexus/src/lib/request-handler/request-handler.relay.test.ts rename to packages/nexus/src/fetch/request-handler/fetch-request-handler.relay.test.ts index fcfbeab..704aa7b 100644 --- a/packages/nexus/src/lib/request-handler/request-handler.relay.test.ts +++ b/packages/nexus/src/fetch/request-handler/fetch-request-handler.relay.test.ts @@ -1,9 +1,14 @@ import { afterAll, afterEach, beforeAll, describe, expect, it } from "vitest"; import { setupServer } from "msw/node"; -import { Config } from "../config"; +import { Config } from "../../lib/config"; import { handlers } from "../../../tests/mock-server-handlers"; import { retry } from "../../../tests/utils"; -import { AbstractRequestHandler } from "./abstract-request-handler"; +import { + defaultChainRegistry, + defaultServiceProviderRegistry, +} from "../../lib/setup/data"; +import { RpcEndpointPoolFactory } from "../../lib/rpc-endpoint/rpc-endpoint-pool-factory"; +import { RequestHandler } from "./request-handler"; const sharedConfig = { globalAccessKey: "some-key", @@ -31,7 +36,6 @@ const configWithNoRecovery = new Config({ }); const blockNumberRequestHelper = (config: Config) => { - const requestHandler = new AbstractRequestHandler({ config }); const request = new Request( "https://my-test-rpc-provider.com/eth/mainnet?key=some-key", { @@ -44,11 +48,21 @@ const blockNumberRequestHelper = (config: Config) => { }), } ); + const requestHandler = new RequestHandler({ + config, + request, + chainRegistry: defaultChainRegistry, + rpcEndpointPoolFactory: new RpcEndpointPoolFactory({ + chainRegistry: defaultChainRegistry, + config, + serviceProviderRegistry: defaultServiceProviderRegistry, + }), + }); - return requestHandler.handleFetch(request); + return requestHandler.handle(); }; -describe("request handler - relay", () => { +describe("fetch request handler - relay", () => { describe("all providers up", () => { const server = setupServer( handlers.alchemyReturnsBlockNumber, diff --git a/packages/nexus/src/lib/request-handler/request-handler.status.test.ts b/packages/nexus/src/fetch/request-handler/fetch-request-handler.status.test.ts similarity index 88% rename from packages/nexus/src/lib/request-handler/request-handler.status.test.ts rename to packages/nexus/src/fetch/request-handler/fetch-request-handler.status.test.ts index 429f406..6900fbd 100644 --- a/packages/nexus/src/lib/request-handler/request-handler.status.test.ts +++ b/packages/nexus/src/fetch/request-handler/fetch-request-handler.status.test.ts @@ -1,21 +1,35 @@ import { afterAll, afterEach, beforeAll, describe, expect, it } from "vitest"; import { setupServer } from "msw/node"; -import { Config } from "../config"; +import { Config } from "../../lib/config"; import { handlers } from "../../../tests/mock-server-handlers"; -import { AbstractRequestHandler } from "./abstract-request-handler"; +import { + defaultChainRegistry, + defaultServiceProviderRegistry, +} from "../../lib/setup/data"; +import { RpcEndpointPoolFactory } from "../../lib/rpc-endpoint/rpc-endpoint-pool-factory"; +import { RequestHandler } from "./request-handler"; export const requestHelper = async (endpoint: string, config: Config) => { - const requestHandler = new AbstractRequestHandler({ config }); const request = new Request(`https://my-test-rpc-provider.com${endpoint}`, { method: "GET", }); - const response = await requestHandler.handleFetch(request); + const requestHandler = new RequestHandler({ + config, + request, + chainRegistry: defaultChainRegistry, + rpcEndpointPoolFactory: new RpcEndpointPoolFactory({ + config, + chainRegistry: defaultChainRegistry, + serviceProviderRegistry: defaultServiceProviderRegistry, + }), + }); + const response = await requestHandler.handle(); const data: unknown = await response.json(); return data; }; -describe("request handler - status", () => { +describe("fetch request handler - status", () => { describe("providers are up", () => { const server = setupServer( handlers.alchemyReturnsBlockNumber, diff --git a/packages/nexus/src/fetch/request-handler/index.ts b/packages/nexus/src/fetch/request-handler/index.ts new file mode 100644 index 0000000..2e1ec7a --- /dev/null +++ b/packages/nexus/src/fetch/request-handler/index.ts @@ -0,0 +1 @@ +export * from "./request-handler"; diff --git a/packages/nexus/src/lib/request-handler/fetch-request-handler.ts b/packages/nexus/src/fetch/request-handler/request-handler.ts similarity index 78% rename from packages/nexus/src/lib/request-handler/fetch-request-handler.ts rename to packages/nexus/src/fetch/request-handler/request-handler.ts index fa1460c..7320275 100644 --- a/packages/nexus/src/lib/request-handler/fetch-request-handler.ts +++ b/packages/nexus/src/fetch/request-handler/request-handler.ts @@ -1,14 +1,14 @@ -import type { ChainRegistry } from "../chain/chain-registry"; -import type { Config } from "../config"; -import { matchPath } from "../routes/routes"; -import { JsonRPCRequestSchema } from "../rpc-endpoint/json-rpc-types"; -import type { RpcEndpointPoolFactory } from "../rpc-endpoint/rpc-endpoint-pool-factory"; -import type { Nexus } from "../nexus"; -import { RpcProxyContext } from "./rpc-proxy-context"; -import type { NexusPreResponse } from "./abstract-request-handler"; -import { AbstractRequestHandler } from "./abstract-request-handler"; - -export class FetchRequestHandler extends AbstractRequestHandler { +import type { ChainRegistry } from "../../lib/chain/chain-registry"; +import type { Config } from "../../lib/config"; +import { matchPath } from "../../lib/routes"; +import { JsonRPCRequestSchema } from "../../lib/rpc-endpoint/json-rpc-types"; +import type { RpcEndpointPoolFactory } from "../../lib/rpc-endpoint/rpc-endpoint-pool-factory"; +import type { Nexus } from "../../lib/nexus"; +import { RpcProxyContext } from "../../lib/request-handler/rpc-proxy-context"; +import type { NexusPreResponse } from "../../lib/request-handler/abstract-request-handler"; +import { AbstractRequestHandler } from "../../lib/request-handler/abstract-request-handler"; + +export class RequestHandler extends AbstractRequestHandler { private readonly request: Request; constructor(params: { @@ -22,7 +22,7 @@ export class FetchRequestHandler extends AbstractRequestHandler { } public static init(nexus: Nexus, request: Request) { - return new FetchRequestHandler({ + return new RequestHandler({ config: nexus.config, chainRegistry: nexus.chainRegistry, rpcEndpointPoolFactory: nexus.rpcEndpointPoolFactory, diff --git a/packages/nexus/src/lib/index.ts b/packages/nexus/src/lib/index.ts new file mode 100644 index 0000000..a34f7db --- /dev/null +++ b/packages/nexus/src/lib/index.ts @@ -0,0 +1,2 @@ +export * from "./nexus"; +export * from "./config"; diff --git a/packages/nexus/src/lib/routes/index.ts b/packages/nexus/src/lib/routes/index.ts new file mode 100644 index 0000000..9bf9b1b --- /dev/null +++ b/packages/nexus/src/lib/routes/index.ts @@ -0,0 +1 @@ +export * from "./routes"; diff --git a/packages/nexus/src/lib/rpc-endpoint/rpc-endpoint-pool-factory.test.ts b/packages/nexus/src/lib/rpc-endpoint/rpc-endpoint-pool-factory.test.ts index b63ee12..4a900d7 100644 --- a/packages/nexus/src/lib/rpc-endpoint/rpc-endpoint-pool-factory.test.ts +++ b/packages/nexus/src/lib/rpc-endpoint/rpc-endpoint-pool-factory.test.ts @@ -8,6 +8,8 @@ import { RpcEndpointPoolFactory } from "./rpc-endpoint-pool-factory"; describe("provider factory", () => { const factory = new RpcEndpointPoolFactory({ + chainRegistry: defaultChainRegistry, + serviceProviderRegistry: defaultServiceProviderRegistry, config: new Config({ providers: { alchemy: { diff --git a/packages/nexus/src/fetch.ts b/packages/nexus/src/node/index.ts similarity index 56% rename from packages/nexus/src/fetch.ts rename to packages/nexus/src/node/index.ts index 4670f8c..7ca401b 100644 --- a/packages/nexus/src/fetch.ts +++ b/packages/nexus/src/node/index.ts @@ -1,6 +1,4 @@ // TODO: how can i let the user specify the load balancing weights per service provider or even per custom provider url? // TODO: how do i build fallback logic? for example, `if ankr fails, fallback to infura`? -export * from "./lib/config"; -export * from "./lib/nexus"; -export { FetchRequestHandler as RequestHandler } from "./lib/request-handler/fetch-request-handler"; +export { RequestHandler } from "./request-handler"; diff --git a/packages/nexus/src/node/request-handler/index.ts b/packages/nexus/src/node/request-handler/index.ts new file mode 100644 index 0000000..2e1ec7a --- /dev/null +++ b/packages/nexus/src/node/request-handler/index.ts @@ -0,0 +1 @@ +export * from "./request-handler"; diff --git a/packages/nexus/src/lib/request-handler/node-request-handler.ts b/packages/nexus/src/node/request-handler/request-handler.ts similarity index 86% rename from packages/nexus/src/lib/request-handler/node-request-handler.ts rename to packages/nexus/src/node/request-handler/request-handler.ts index 649506a..d6b2433 100644 --- a/packages/nexus/src/lib/request-handler/node-request-handler.ts +++ b/packages/nexus/src/node/request-handler/request-handler.ts @@ -1,17 +1,17 @@ import type http from "node:http"; import NodeURL from "node:url"; import querystring from "node:querystring"; -import type { Config } from "../config"; -import type { ChainRegistry } from "../chain/chain-registry"; -import { matchPath } from "../routes/routes"; -import { JsonRPCRequestSchema } from "../rpc-endpoint/json-rpc-types"; -import type { RpcEndpointPoolFactory } from "../rpc-endpoint/rpc-endpoint-pool-factory"; -import type { Nexus } from "../nexus"; -import { RpcProxyContext } from "./rpc-proxy-context"; -import type { NexusPreResponse } from "./abstract-request-handler"; -import { AbstractRequestHandler } from "./abstract-request-handler"; - -export class NodeRequestHandler extends AbstractRequestHandler { +import type { Config } from "../../lib/config"; +import type { ChainRegistry } from "../../lib/chain/chain-registry"; +import { matchPath } from "../../lib/routes"; +import { JsonRPCRequestSchema } from "../../lib/rpc-endpoint/json-rpc-types"; +import type { RpcEndpointPoolFactory } from "../../lib/rpc-endpoint/rpc-endpoint-pool-factory"; +import type { Nexus } from "../../lib/nexus"; +import { RpcProxyContext } from "../../lib/request-handler/rpc-proxy-context"; +import type { NexusPreResponse } from "../../lib/request-handler/abstract-request-handler"; +import { AbstractRequestHandler } from "../../lib/request-handler/abstract-request-handler"; + +export class RequestHandler extends AbstractRequestHandler { private readonly req: http.IncomingMessage; private readonly res: http.ServerResponse; @@ -32,7 +32,7 @@ export class NodeRequestHandler extends AbstractRequestHandler { req: http.IncomingMessage, res: http.ServerResponse ) { - return new NodeRequestHandler({ + return new RequestHandler({ config: nexus.config, chainRegistry: nexus.chainRegistry, rpcEndpointPoolFactory: nexus.rpcEndpointPoolFactory, diff --git a/packages/nexus/tsup.config.ts b/packages/nexus/tsup.config.ts index 25736c4..42c754b 100644 --- a/packages/nexus/tsup.config.ts +++ b/packages/nexus/tsup.config.ts @@ -3,7 +3,7 @@ import { defineConfig } from "tsup"; export default defineConfig((options: Options) => ({ treeshake: true, - entry: ["./src/fetch.ts", "./src/node.ts"], + entry: ["./src/fetch/index.ts", "./src/node/index.ts", "./src/lib/index.ts"], format: ["cjs"], dts: true, minify: true, From 2a5199e1463aab36fe83e6c022f8cf510c7dca41 Mon Sep 17 00:00:00 2001 From: Kerem Kazan Date: Sun, 12 Nov 2023 17:02:17 -0500 Subject: [PATCH 21/41] worker uses the fetch import now --- examples/nexus-worker/src/index.ts | 3 ++- packages/nexus/src/node/request-handler/request-handler.ts | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/examples/nexus-worker/src/index.ts b/examples/nexus-worker/src/index.ts index 4e26de7..0f77789 100644 --- a/examples/nexus-worker/src/index.ts +++ b/examples/nexus-worker/src/index.ts @@ -1,4 +1,5 @@ -import { Nexus, RequestHandler } from "@whatsgood/nexus/fetch"; +import { Nexus } from "@whatsgood/nexus"; +import { RequestHandler } from "@whatsgood/nexus/fetch"; // TODO: add config alerts to indicate that the key access is incomplete // TODO: add onboarding & UX. (setup admin access, login, etc) diff --git a/packages/nexus/src/node/request-handler/request-handler.ts b/packages/nexus/src/node/request-handler/request-handler.ts index d6b2433..0d0f709 100644 --- a/packages/nexus/src/node/request-handler/request-handler.ts +++ b/packages/nexus/src/node/request-handler/request-handler.ts @@ -11,6 +11,8 @@ import { RpcProxyContext } from "../../lib/request-handler/rpc-proxy-context"; import type { NexusPreResponse } from "../../lib/request-handler/abstract-request-handler"; import { AbstractRequestHandler } from "../../lib/request-handler/abstract-request-handler"; +// TODO: write handler tests for the node handler + export class RequestHandler extends AbstractRequestHandler { private readonly req: http.IncomingMessage; private readonly res: http.ServerResponse; From aa75e6b6c7e87c4358da51820d3df9f6401f8636 Mon Sep 17 00:00:00 2001 From: Kerem Kazan Date: Sun, 12 Nov 2023 17:18:22 -0500 Subject: [PATCH 22/41] passing nexus object for config + registries --- examples/nexus-worker/src/index.ts | 3 +- .../fetch-request-handler.relay.test.ts | 18 ++------- .../fetch-request-handler.status.test.ts | 18 ++------- .../fetch/request-handler/request-handler.ts | 33 ++++----------- .../abstract-request-handler.ts | 18 +-------- .../node/request-handler/request-handler.ts | 40 ++++--------------- 6 files changed, 26 insertions(+), 104 deletions(-) diff --git a/examples/nexus-worker/src/index.ts b/examples/nexus-worker/src/index.ts index 0f77789..5fbfb02 100644 --- a/examples/nexus-worker/src/index.ts +++ b/examples/nexus-worker/src/index.ts @@ -13,6 +13,7 @@ export default { const nexus = new Nexus({ env, }); - return RequestHandler.init(nexus, request).handle(); + const requestHandler = new RequestHandler(nexus, request); + return requestHandler.handle(); }, }; diff --git a/packages/nexus/src/fetch/request-handler/fetch-request-handler.relay.test.ts b/packages/nexus/src/fetch/request-handler/fetch-request-handler.relay.test.ts index 704aa7b..a1d7653 100644 --- a/packages/nexus/src/fetch/request-handler/fetch-request-handler.relay.test.ts +++ b/packages/nexus/src/fetch/request-handler/fetch-request-handler.relay.test.ts @@ -3,11 +3,7 @@ import { setupServer } from "msw/node"; import { Config } from "../../lib/config"; import { handlers } from "../../../tests/mock-server-handlers"; import { retry } from "../../../tests/utils"; -import { - defaultChainRegistry, - defaultServiceProviderRegistry, -} from "../../lib/setup/data"; -import { RpcEndpointPoolFactory } from "../../lib/rpc-endpoint/rpc-endpoint-pool-factory"; +import { Nexus } from "../../lib"; import { RequestHandler } from "./request-handler"; const sharedConfig = { @@ -36,6 +32,7 @@ const configWithNoRecovery = new Config({ }); const blockNumberRequestHelper = (config: Config) => { + const nexus = new Nexus(config); const request = new Request( "https://my-test-rpc-provider.com/eth/mainnet?key=some-key", { @@ -48,16 +45,7 @@ const blockNumberRequestHelper = (config: Config) => { }), } ); - const requestHandler = new RequestHandler({ - config, - request, - chainRegistry: defaultChainRegistry, - rpcEndpointPoolFactory: new RpcEndpointPoolFactory({ - chainRegistry: defaultChainRegistry, - config, - serviceProviderRegistry: defaultServiceProviderRegistry, - }), - }); + const requestHandler = new RequestHandler(nexus, request); return requestHandler.handle(); }; diff --git a/packages/nexus/src/fetch/request-handler/fetch-request-handler.status.test.ts b/packages/nexus/src/fetch/request-handler/fetch-request-handler.status.test.ts index 6900fbd..b5cacfe 100644 --- a/packages/nexus/src/fetch/request-handler/fetch-request-handler.status.test.ts +++ b/packages/nexus/src/fetch/request-handler/fetch-request-handler.status.test.ts @@ -2,27 +2,15 @@ import { afterAll, afterEach, beforeAll, describe, expect, it } from "vitest"; import { setupServer } from "msw/node"; import { Config } from "../../lib/config"; import { handlers } from "../../../tests/mock-server-handlers"; -import { - defaultChainRegistry, - defaultServiceProviderRegistry, -} from "../../lib/setup/data"; -import { RpcEndpointPoolFactory } from "../../lib/rpc-endpoint/rpc-endpoint-pool-factory"; +import { Nexus } from "../../lib"; import { RequestHandler } from "./request-handler"; export const requestHelper = async (endpoint: string, config: Config) => { + const nexus = new Nexus(config); const request = new Request(`https://my-test-rpc-provider.com${endpoint}`, { method: "GET", }); - const requestHandler = new RequestHandler({ - config, - request, - chainRegistry: defaultChainRegistry, - rpcEndpointPoolFactory: new RpcEndpointPoolFactory({ - config, - chainRegistry: defaultChainRegistry, - serviceProviderRegistry: defaultServiceProviderRegistry, - }), - }); + const requestHandler = new RequestHandler(nexus, request); const response = await requestHandler.handle(); const data: unknown = await response.json(); diff --git a/packages/nexus/src/fetch/request-handler/request-handler.ts b/packages/nexus/src/fetch/request-handler/request-handler.ts index 7320275..947c28c 100644 --- a/packages/nexus/src/fetch/request-handler/request-handler.ts +++ b/packages/nexus/src/fetch/request-handler/request-handler.ts @@ -1,33 +1,16 @@ -import type { ChainRegistry } from "../../lib/chain/chain-registry"; -import type { Config } from "../../lib/config"; import { matchPath } from "../../lib/routes"; import { JsonRPCRequestSchema } from "../../lib/rpc-endpoint/json-rpc-types"; -import type { RpcEndpointPoolFactory } from "../../lib/rpc-endpoint/rpc-endpoint-pool-factory"; import type { Nexus } from "../../lib/nexus"; import { RpcProxyContext } from "../../lib/request-handler/rpc-proxy-context"; import type { NexusPreResponse } from "../../lib/request-handler/abstract-request-handler"; import { AbstractRequestHandler } from "../../lib/request-handler/abstract-request-handler"; export class RequestHandler extends AbstractRequestHandler { - private readonly request: Request; - - constructor(params: { - config: Config; - chainRegistry: ChainRegistry; - rpcEndpointPoolFactory: RpcEndpointPoolFactory; - request: Request; - }) { - super(params); - this.request = params.request; - } - - public static init(nexus: Nexus, request: Request) { - return new RequestHandler({ - config: nexus.config, - chainRegistry: nexus.chainRegistry, - rpcEndpointPoolFactory: nexus.rpcEndpointPoolFactory, - request, - }); + constructor( + protected readonly nexus: Nexus, + private readonly request: Request + ) { + super(nexus); } private async parseJSONRpcRequest() { @@ -81,10 +64,10 @@ export class RequestHandler extends AbstractRequestHandler { const route = matchPath(requestUrl.pathname); const chain = route - ? this.chainRegistry.getByOptionalParams(route.params) + ? this.nexus.chainRegistry.getByOptionalParams(route.params) : undefined; const pool = chain - ? this.rpcEndpointPoolFactory.fromChain(chain) + ? this.nexus.rpcEndpointPoolFactory.fromChain(chain) : undefined; const jsonRPCRequestParseResult = await this.parseJSONRpcRequest(); @@ -93,7 +76,7 @@ export class RequestHandler extends AbstractRequestHandler { return new RpcProxyContext({ pool, - config: this.config, + config: this.nexus.config, chain, path: requestPath, clientAccessKey, diff --git a/packages/nexus/src/lib/request-handler/abstract-request-handler.ts b/packages/nexus/src/lib/request-handler/abstract-request-handler.ts index 19c8872..a46558b 100644 --- a/packages/nexus/src/lib/request-handler/abstract-request-handler.ts +++ b/packages/nexus/src/lib/request-handler/abstract-request-handler.ts @@ -1,6 +1,4 @@ -import type { ChainRegistry } from "../chain/chain-registry"; -import type { Config } from "../config"; -import type { RpcEndpointPoolFactory } from "../rpc-endpoint/rpc-endpoint-pool-factory"; +import type { Nexus } from "../nexus"; import type { RpcProxyContext } from "./rpc-proxy-context"; export interface NexusPreResponse { @@ -10,19 +8,7 @@ export interface NexusPreResponse { } export abstract class AbstractRequestHandler { - protected config: Config; - protected chainRegistry: ChainRegistry; - protected rpcEndpointPoolFactory: RpcEndpointPoolFactory; - - constructor(params: { - config: Config; - chainRegistry: ChainRegistry; - rpcEndpointPoolFactory: RpcEndpointPoolFactory; - }) { - this.config = params.config; - this.chainRegistry = params.chainRegistry; - this.rpcEndpointPoolFactory = params.rpcEndpointPoolFactory; - } + constructor(protected readonly nexus: Nexus) {} public async handle(): Promise { const context = await this.getContext(); diff --git a/packages/nexus/src/node/request-handler/request-handler.ts b/packages/nexus/src/node/request-handler/request-handler.ts index 0d0f709..1ff36e4 100644 --- a/packages/nexus/src/node/request-handler/request-handler.ts +++ b/packages/nexus/src/node/request-handler/request-handler.ts @@ -1,11 +1,8 @@ import type http from "node:http"; import NodeURL from "node:url"; import querystring from "node:querystring"; -import type { Config } from "../../lib/config"; -import type { ChainRegistry } from "../../lib/chain/chain-registry"; import { matchPath } from "../../lib/routes"; import { JsonRPCRequestSchema } from "../../lib/rpc-endpoint/json-rpc-types"; -import type { RpcEndpointPoolFactory } from "../../lib/rpc-endpoint/rpc-endpoint-pool-factory"; import type { Nexus } from "../../lib/nexus"; import { RpcProxyContext } from "../../lib/request-handler/rpc-proxy-context"; import type { NexusPreResponse } from "../../lib/request-handler/abstract-request-handler"; @@ -14,33 +11,12 @@ import { AbstractRequestHandler } from "../../lib/request-handler/abstract-reque // TODO: write handler tests for the node handler export class RequestHandler extends AbstractRequestHandler { - private readonly req: http.IncomingMessage; - private readonly res: http.ServerResponse; - - constructor(params: { - config: Config; - chainRegistry: ChainRegistry; - rpcEndpointPoolFactory: RpcEndpointPoolFactory; - req: http.IncomingMessage; - res: http.ServerResponse; - }) { - super(params); - this.req = params.req; - this.res = params.res; - } - - public static init( - nexus: Nexus, - req: http.IncomingMessage, - res: http.ServerResponse + constructor( + protected readonly nexus: Nexus, + private readonly req: http.IncomingMessage, + private readonly res: http.ServerResponse ) { - return new RequestHandler({ - config: nexus.config, - chainRegistry: nexus.chainRegistry, - rpcEndpointPoolFactory: nexus.rpcEndpointPoolFactory, - req, - res, - }); + super(nexus); } protected handlePreResponse(preResponse: NexusPreResponse) { @@ -180,10 +156,10 @@ export class RequestHandler extends AbstractRequestHandler { const route = requestPath ? matchPath(requestPath) : undefined; const chain = route - ? this.chainRegistry.getByOptionalParams(route.params) + ? this.nexus.chainRegistry.getByOptionalParams(route.params) : undefined; const pool = chain - ? this.rpcEndpointPoolFactory.fromChain(chain) + ? this.nexus.rpcEndpointPoolFactory.fromChain(chain) : undefined; const clientAccessKey = query ? querystring.parse(query).key : undefined; @@ -192,7 +168,7 @@ export class RequestHandler extends AbstractRequestHandler { return new RpcProxyContext({ pool, - config: this.config, + config: this.nexus.config, httpMethod: this.req.method, chain, path: requestPath || "", // TODO: remove this hack, make this more robust From 677e0488c92acc0fec3bada6feff9a43f201720c Mon Sep 17 00:00:00 2001 From: Kerem Kazan Date: Sun, 12 Nov 2023 18:48:32 -0500 Subject: [PATCH 23/41] fixed long relative import paths --- packages/eslint-config-custom/library.js | 4 ++++ packages/eslint-config-custom/package.json | 1 + .../fetch-request-handler.relay.test.ts | 8 ++++---- .../fetch-request-handler.status.test.ts | 6 +++--- .../src/fetch/request-handler/request-handler.ts | 12 ++++++------ .../src/node/request-handler/request-handler.ts | 12 ++++++------ .../nexus/{tests => test}/mock-server-handlers.ts | 0 packages/nexus/{tests => test}/setup.ts | 0 packages/nexus/{tests => test}/utils.ts | 0 packages/nexus/tsconfig.json | 10 ++++++++-- packages/nexus/vitest.config.ts | 6 +++++- pnpm-lock.yaml | 3 +++ 12 files changed, 40 insertions(+), 22 deletions(-) rename packages/nexus/{tests => test}/mock-server-handlers.ts (100%) rename packages/nexus/{tests => test}/setup.ts (100%) rename packages/nexus/{tests => test}/utils.ts (100%) diff --git a/packages/eslint-config-custom/library.js b/packages/eslint-config-custom/library.js index 9470cbc..193d597 100644 --- a/packages/eslint-config-custom/library.js +++ b/packages/eslint-config-custom/library.js @@ -43,6 +43,10 @@ module.exports = { "no-console": "off", "no-only-tests/no-only-tests": "error", "import/no-default-export": "warn", + // TODO: this should actually be on, but it doesn't work when + // baseURL + paths are used in tsconfig.json. Find a way to + // make it work. + "import/no-extraneous-dependencies": "off", "no-restricted-syntax": [ "error", { diff --git a/packages/eslint-config-custom/package.json b/packages/eslint-config-custom/package.json index f4cb4a7..578832f 100644 --- a/packages/eslint-config-custom/package.json +++ b/packages/eslint-config-custom/package.json @@ -6,6 +6,7 @@ "devDependencies": { "@vercel/style-guide": "^5.0.0", "eslint-config-turbo": "^1.10.12", + "eslint-import-resolver-typescript": "^3.6.1", "eslint-plugin-no-only-tests": "^3.1.0" } } diff --git a/packages/nexus/src/fetch/request-handler/fetch-request-handler.relay.test.ts b/packages/nexus/src/fetch/request-handler/fetch-request-handler.relay.test.ts index a1d7653..44650f6 100644 --- a/packages/nexus/src/fetch/request-handler/fetch-request-handler.relay.test.ts +++ b/packages/nexus/src/fetch/request-handler/fetch-request-handler.relay.test.ts @@ -1,9 +1,9 @@ import { afterAll, afterEach, beforeAll, describe, expect, it } from "vitest"; import { setupServer } from "msw/node"; -import { Config } from "../../lib/config"; -import { handlers } from "../../../tests/mock-server-handlers"; -import { retry } from "../../../tests/utils"; -import { Nexus } from "../../lib"; +import { handlers } from "@test/mock-server-handlers"; +import { retry } from "@test/utils"; +import { Config } from "@lib/config"; +import { Nexus } from "@lib/nexus"; import { RequestHandler } from "./request-handler"; const sharedConfig = { diff --git a/packages/nexus/src/fetch/request-handler/fetch-request-handler.status.test.ts b/packages/nexus/src/fetch/request-handler/fetch-request-handler.status.test.ts index b5cacfe..a60b154 100644 --- a/packages/nexus/src/fetch/request-handler/fetch-request-handler.status.test.ts +++ b/packages/nexus/src/fetch/request-handler/fetch-request-handler.status.test.ts @@ -1,8 +1,8 @@ import { afterAll, afterEach, beforeAll, describe, expect, it } from "vitest"; import { setupServer } from "msw/node"; -import { Config } from "../../lib/config"; -import { handlers } from "../../../tests/mock-server-handlers"; -import { Nexus } from "../../lib"; +import { handlers } from "@test/mock-server-handlers"; +import { Config } from "@lib/config"; +import { Nexus } from "@lib/nexus"; import { RequestHandler } from "./request-handler"; export const requestHelper = async (endpoint: string, config: Config) => { diff --git a/packages/nexus/src/fetch/request-handler/request-handler.ts b/packages/nexus/src/fetch/request-handler/request-handler.ts index 947c28c..abaf18a 100644 --- a/packages/nexus/src/fetch/request-handler/request-handler.ts +++ b/packages/nexus/src/fetch/request-handler/request-handler.ts @@ -1,9 +1,9 @@ -import { matchPath } from "../../lib/routes"; -import { JsonRPCRequestSchema } from "../../lib/rpc-endpoint/json-rpc-types"; -import type { Nexus } from "../../lib/nexus"; -import { RpcProxyContext } from "../../lib/request-handler/rpc-proxy-context"; -import type { NexusPreResponse } from "../../lib/request-handler/abstract-request-handler"; -import { AbstractRequestHandler } from "../../lib/request-handler/abstract-request-handler"; +import { JsonRPCRequestSchema } from "@lib/rpc-endpoint/json-rpc-types"; +import type { Nexus } from "@lib/nexus"; +import { RpcProxyContext } from "@lib/request-handler/rpc-proxy-context"; +import type { NexusPreResponse } from "@lib/request-handler/abstract-request-handler"; +import { AbstractRequestHandler } from "@lib/request-handler/abstract-request-handler"; +import { matchPath } from "@lib/routes"; export class RequestHandler extends AbstractRequestHandler { constructor( diff --git a/packages/nexus/src/node/request-handler/request-handler.ts b/packages/nexus/src/node/request-handler/request-handler.ts index 1ff36e4..288465a 100644 --- a/packages/nexus/src/node/request-handler/request-handler.ts +++ b/packages/nexus/src/node/request-handler/request-handler.ts @@ -1,12 +1,12 @@ import type http from "node:http"; import NodeURL from "node:url"; import querystring from "node:querystring"; -import { matchPath } from "../../lib/routes"; -import { JsonRPCRequestSchema } from "../../lib/rpc-endpoint/json-rpc-types"; -import type { Nexus } from "../../lib/nexus"; -import { RpcProxyContext } from "../../lib/request-handler/rpc-proxy-context"; -import type { NexusPreResponse } from "../../lib/request-handler/abstract-request-handler"; -import { AbstractRequestHandler } from "../../lib/request-handler/abstract-request-handler"; +import { matchPath } from "@lib/routes"; +import { JsonRPCRequestSchema } from "@lib/rpc-endpoint/json-rpc-types"; +import type { Nexus } from "@lib/nexus"; +import { RpcProxyContext } from "@lib/request-handler/rpc-proxy-context"; +import type { NexusPreResponse } from "@lib/request-handler/abstract-request-handler"; +import { AbstractRequestHandler } from "@lib/request-handler/abstract-request-handler"; // TODO: write handler tests for the node handler diff --git a/packages/nexus/tests/mock-server-handlers.ts b/packages/nexus/test/mock-server-handlers.ts similarity index 100% rename from packages/nexus/tests/mock-server-handlers.ts rename to packages/nexus/test/mock-server-handlers.ts diff --git a/packages/nexus/tests/setup.ts b/packages/nexus/test/setup.ts similarity index 100% rename from packages/nexus/tests/setup.ts rename to packages/nexus/test/setup.ts diff --git a/packages/nexus/tests/utils.ts b/packages/nexus/test/utils.ts similarity index 100% rename from packages/nexus/tests/utils.ts rename to packages/nexus/test/utils.ts diff --git a/packages/nexus/tsconfig.json b/packages/nexus/tsconfig.json index 6646cf0..5caed8c 100644 --- a/packages/nexus/tsconfig.json +++ b/packages/nexus/tsconfig.json @@ -4,11 +4,17 @@ "include": [ "./src/**/*.ts", "./vitest.config.ts", - "./tests/**/*.ts", + "test/**/*.ts", "tsup.config.ts" ], "compilerOptions": { "module": "CommonJS", - "lib": ["es2017", "dom"] + "lib": ["es2017", "dom"], + "resolveJsonModule": true, + "baseUrl": ".", + "paths": { + "@lib/*": ["./src/lib/*"], + "@test/*": ["./test/*"] + } } } diff --git a/packages/nexus/vitest.config.ts b/packages/nexus/vitest.config.ts index 749b23d..c0c44f4 100644 --- a/packages/nexus/vitest.config.ts +++ b/packages/nexus/vitest.config.ts @@ -2,7 +2,11 @@ import { defineConfig } from "vitest/config"; export default defineConfig({ test: { - setupFiles: ["./tests/setup.ts"], + setupFiles: ["./test/setup.ts"], + alias: { + "@lib": "./src/lib", + "@test": "./test", + }, silent: true, }, }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ea2d613..4701d6c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -66,6 +66,9 @@ importers: eslint-config-turbo: specifier: ^1.10.12 version: 1.10.12(eslint@8.50.0) + eslint-import-resolver-typescript: + specifier: ^3.6.1 + version: 3.6.1(@typescript-eslint/parser@6.7.3)(eslint-plugin-import@2.28.1)(eslint@8.50.0) eslint-plugin-no-only-tests: specifier: ^3.1.0 version: 3.1.0 From 266f6285b21f7f2b4e745689ac5906cadb6bf2dd Mon Sep 17 00:00:00 2001 From: Kerem Kazan Date: Mon, 13 Nov 2023 07:42:45 -0500 Subject: [PATCH 24/41] updated location of the cloudflare example --- docs/recipes/cloudflare-worker.mdx | 2 +- examples/{nexus-worker => cloudflare-worker}/CHANGELOG.md | 2 +- examples/{nexus-worker => cloudflare-worker}/package.json | 2 +- examples/{nexus-worker => cloudflare-worker}/src/index.ts | 0 examples/{nexus-worker => cloudflare-worker}/tsconfig.json | 0 examples/{nexus-worker => cloudflare-worker}/wrangler.toml | 0 pnpm-lock.yaml | 2 +- 7 files changed, 4 insertions(+), 4 deletions(-) rename examples/{nexus-worker => cloudflare-worker}/CHANGELOG.md (91%) rename examples/{nexus-worker => cloudflare-worker}/package.json (91%) rename examples/{nexus-worker => cloudflare-worker}/src/index.ts (100%) rename examples/{nexus-worker => cloudflare-worker}/tsconfig.json (100%) rename examples/{nexus-worker => cloudflare-worker}/wrangler.toml (100%) diff --git a/docs/recipes/cloudflare-worker.mdx b/docs/recipes/cloudflare-worker.mdx index 54eecb4..193ea75 100644 --- a/docs/recipes/cloudflare-worker.mdx +++ b/docs/recipes/cloudflare-worker.mdx @@ -13,7 +13,7 @@ description: "Deploy Nexus as a Cloudflare Worker in under 5 minutes" If you want to skip this guide, follow [this - link](https://github.com/whats-good/nexus/tree/main/examples/nexus-worker) for + link](https://github.com/whats-good/nexus/tree/main/examples/cloudflare-worker) for a complete codebase example. diff --git a/examples/nexus-worker/CHANGELOG.md b/examples/cloudflare-worker/CHANGELOG.md similarity index 91% rename from examples/nexus-worker/CHANGELOG.md rename to examples/cloudflare-worker/CHANGELOG.md index 58bd0c4..6e7bb98 100644 --- a/examples/nexus-worker/CHANGELOG.md +++ b/examples/cloudflare-worker/CHANGELOG.md @@ -1,4 +1,4 @@ -# @whatsgood/nexus-worker +# @whatsgood/example-cloudflare-worker ## 0.1.14 diff --git a/examples/nexus-worker/package.json b/examples/cloudflare-worker/package.json similarity index 91% rename from examples/nexus-worker/package.json rename to examples/cloudflare-worker/package.json index c7484c3..1570eaa 100644 --- a/examples/nexus-worker/package.json +++ b/examples/cloudflare-worker/package.json @@ -1,5 +1,5 @@ { - "name": "@whatsgood/nexus-worker", + "name": "@whatsgood/example-cloudflare-worker", "version": "0.1.14", "private": true, "author": { diff --git a/examples/nexus-worker/src/index.ts b/examples/cloudflare-worker/src/index.ts similarity index 100% rename from examples/nexus-worker/src/index.ts rename to examples/cloudflare-worker/src/index.ts diff --git a/examples/nexus-worker/tsconfig.json b/examples/cloudflare-worker/tsconfig.json similarity index 100% rename from examples/nexus-worker/tsconfig.json rename to examples/cloudflare-worker/tsconfig.json diff --git a/examples/nexus-worker/wrangler.toml b/examples/cloudflare-worker/wrangler.toml similarity index 100% rename from examples/nexus-worker/wrangler.toml rename to examples/cloudflare-worker/wrangler.toml diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4701d6c..ae4d9a1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -33,7 +33,7 @@ importers: specifier: 5.2.2 version: 5.2.2 - examples/nexus-worker: + examples/cloudflare-worker: dependencies: '@whatsgood/nexus': specifier: workspace:* From ee2a0a9a3e13e30e6eee99290d7413b792def620 Mon Sep 17 00:00:00 2001 From: Kerem Kazan Date: Mon, 13 Nov 2023 07:43:02 -0500 Subject: [PATCH 25/41] docs is a package now --- docs/package.json | 3 +++ pnpm-workspace.yaml | 1 + 2 files changed, 4 insertions(+) diff --git a/docs/package.json b/docs/package.json index e234c5e..f8aad09 100644 --- a/docs/package.json +++ b/docs/package.json @@ -7,6 +7,9 @@ "scripts": { "docs:dev": "mintlify dev" }, + "devDependencies": { + "mintlify": "4.0.49" + }, "keywords": [], "author": "", "license": "ISC" diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index ab881f7..f364871 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -2,3 +2,4 @@ packages: - "examples/*" - "workers/*" - "packages/*" + - "./*" From b48cb3c7e22ca1a7642cd59647cb35a9cfa27b9e Mon Sep 17 00:00:00 2001 From: Kerem Kazan Date: Mon, 13 Nov 2023 08:12:00 -0500 Subject: [PATCH 26/41] docs updated for tsconfig and multi-entry --- README.md | 5 +- docs/_snippets/warning-module-resolution.mdx | 3 + docs/recipes/cloudflare-worker.mdx | 105 +++++++++---------- examples/cloudflare-worker/src/index.js | 16 +++ examples/cloudflare-worker/tsconfig.json | 10 +- packages/nexus/package.json | 2 +- 6 files changed, 73 insertions(+), 68 deletions(-) create mode 100644 docs/_snippets/warning-module-resolution.mdx create mode 100644 examples/cloudflare-worker/src/index.js diff --git a/README.md b/README.md index 7fe0dd1..e1af53e 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,7 @@ yarn add @whatsgood/nexus // Cloudflare worker example import { Nexus } from "@whatsgood/nexus"; +import { RequestHandler } from "@whatsgood/nexus/fetch"; export default { async fetch( @@ -58,11 +59,11 @@ export default { const nexus = new Nexus({ env, }); - return nexus.requestHandler.handleFetch(request); + const requestHandler = new RequestHandler(nexus, request); + return requestHandler.handle(); }, }; - ``` \ No newline at end of file diff --git a/docs/_snippets/warning-module-resolution.mdx b/docs/_snippets/warning-module-resolution.mdx new file mode 100644 index 0000000..43dd805 --- /dev/null +++ b/docs/_snippets/warning-module-resolution.mdx @@ -0,0 +1,3 @@ + +`@whatsgood/nexus` makes use of [Package entry points](https://nodejs.org/api/packages.html#package-entry-points), which causes `tsc` to throw errors when compiling your project. To fix this, you need to update the `compilerOptions` section of your `tsconfig.json` to set `"moduleResolution": "NodeNext"` and `"module": "NodeNext"` . Note that `Node16` will also work, but `NodeNext` is preferred. + \ No newline at end of file diff --git a/docs/recipes/cloudflare-worker.mdx b/docs/recipes/cloudflare-worker.mdx index 193ea75..d0ceedd 100644 --- a/docs/recipes/cloudflare-worker.mdx +++ b/docs/recipes/cloudflare-worker.mdx @@ -64,60 +64,10 @@ You can now `cd` into your new project directory and open it in your favorite ed } ``` -{/* TODO: add the new tsconfig to make the new exports work */} - ```toml wrangler.toml name = "nexus-rpc-worker" main = "src/index.ts" compatibility_date = "2023-10-30" - -# Variable bindings. These are arbitrary, plaintext strings (similar to environment variables) -# Note: Use secrets to store sensitive data. -# Docs: https://developers.cloudflare.com/workers/platform/environment-variables -# [vars] -# MY_VARIABLE = "production_value" - -# Bind a KV Namespace. Use KV as persistent storage for small key-value pairs. -# Docs: https://developers.cloudflare.com/workers/runtime-apis/kv -# [[kv_namespaces]] -# binding = "MY_KV_NAMESPACE" -# id = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" - -# Bind an R2 Bucket. Use R2 to store arbitrarily large blobs of data, such as files. -# Docs: https://developers.cloudflare.com/r2/api/workers/workers-api-usage/ -# [[r2_buckets]] -# binding = "MY_BUCKET" -# bucket_name = "my-bucket" - -# Bind a Queue producer. Use this binding to schedule an arbitrary task that may be processed later by a Queue consumer. -# Docs: https://developers.cloudflare.com/queues/get-started -# [[queues.producers]] -# binding = "MY_QUEUE" -# queue = "my-queue" - -# Bind a Queue consumer. Queue Consumers can retrieve tasks scheduled by Producers to act on them. -# Docs: https://developers.cloudflare.com/queues/get-started -# [[queues.consumers]] -# queue = "my-queue" - -# Bind another Worker service. Use this binding to call another Worker without network overhead. -# Docs: https://developers.cloudflare.com/workers/platform/services -# [[services]] -# binding = "MY_SERVICE" -# service = "my-service" - -# Bind a Durable Object. Durable objects are a scale-to-zero compute primitive based on the actor model. -# Durable Objects can live for as long as needed. Use these when you need a long-running "server", such as in realtime apps. -# Docs: https://developers.cloudflare.com/workers/runtime-apis/durable-objects -# [[durable_objects.bindings]] -# name = "MY_DURABLE_OBJECT" -# class_name = "MyDurableObject" - -# Durable Object migrations. -# Docs: https://developers.cloudflare.com/workers/learning/using-durable-objects#configure-durable-object-classes-with-migrations -# [[migrations]] -# tag = "v1" -# new_classes = ["MyDurableObject"] ``` ```json tsconfig.json @@ -289,23 +239,42 @@ yarn add @whatsgood/nexus +## Update tsconfig + + + +Open `tsconfig.json` and update the `compilerOptions` to include the following. Feel free to keep other options as you please, as long as they don't conflict with the ones below. + +```json tsconfig.json +{ + "exclude": ["node_modules"], + "include": ["./src/**/*.ts"], + "compilerOptions": { + "target": "es2021", + "lib": ["es2021"], + "types": ["@cloudflare/workers-types"], + "moduleResolution": "NodeNext", + "module": "NodeNext" + } +} +``` + ## Write code Once you have installed Nexus, all you need to do is connect the `RpcProxyResponseHandler` from `@whatsgood/nexus` to your Worker's `fetch` handler. - Note how we're passing the `env` object from the `fetch` handler to the - `RpcProxyResponseHandler`'s `Config` constructor. This is required for Nexus - to be able to resolve `Cloudflare Worker` environment variables - as - `Cloudflare` does not expose these to the `process.env` object. + Note how we pass the `env` object from the `fetch` handler to `Nexus` constructor. This is required for Nexus to be able to resolve `Cloudflare Worker` environment variables - as + `Cloudflare` does not expose these to the `process.env` object. Also note how we import the `RequestHandler` from `@whatsgood/nexus/fetch`, and `Nexus` from `@whatsgood/nexus`. This is to ensure that we are using the correct `RequestHandler` instance for the `Cloudflare Worker` environment, as `Nexus` supports multiple runtimes, which are not always compatible with each other. -For the purposes of this tutorial, update the `"dev"` script in `package.json` from `"dev": wrangler dev` to `"dev": "wrangler dev --port 4005"` +For the purposes of this tutorial, update the `"dev"` script in `package.json` from `"dev": wrangler dev` to `"dev": "wrangler dev --port 4005"`. ```ts src/index.ts import { Nexus } from "@whatsgood/nexus"; +import { RequestHandler } from "@whatsgood/nexus/fetch"; export default { async fetch( @@ -315,10 +284,10 @@ export default { const nexus = new Nexus({ env, }); - return nexus.requestHandler.handleFetch(request); + const requestHandler = new RequestHandler(nexus, request); + return requestHandler.handle(); }, }; - ``` ```json package.json @@ -342,6 +311,26 @@ export default { } ``` +```json tsconfig.json +{ + "exclude": ["node_modules"], + "include": ["./src/**/*.ts"], + "compilerOptions": { + "target": "es2021", + "lib": ["es2021"], + "types": ["@cloudflare/workers-types"], + "moduleResolution": "NodeNext", + "module": "NodeNext" + } +} +``` + +```toml wrangler.toml +name = "nexus-rpc-worker" +main = "src/index.ts" +compatibility_date = "2023-10-30" +``` + ## Deploy @@ -421,7 +410,7 @@ npm run dev ``` ```bash pnpm -pnpm run dev +pnpm dev ``` ```bash yarn diff --git a/examples/cloudflare-worker/src/index.js b/examples/cloudflare-worker/src/index.js new file mode 100644 index 0000000..28e5a8e --- /dev/null +++ b/examples/cloudflare-worker/src/index.js @@ -0,0 +1,16 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const nexus_1 = require("@whatsgood/nexus"); +const fetch_1 = require("@whatsgood/nexus/fetch"); +// TODO: add config alerts to indicate that the key access is incomplete +// TODO: add onboarding & UX. (setup admin access, login, etc) +// TODO: add tests for the worker +exports.default = { + async fetch(request, env) { + const nexus = new nexus_1.Nexus({ + env, + }); + const requestHandler = new fetch_1.RequestHandler(nexus, request); + return requestHandler.handle(); + }, +}; diff --git a/examples/cloudflare-worker/tsconfig.json b/examples/cloudflare-worker/tsconfig.json index a73d933..70f1e6b 100644 --- a/examples/cloudflare-worker/tsconfig.json +++ b/examples/cloudflare-worker/tsconfig.json @@ -1,15 +1,11 @@ { - "extends": "tsconfig/base.json", - "exclude": ["dist", "build", "node_modules"], + "exclude": ["node_modules"], "include": ["./src/**/*.ts"], "compilerOptions": { "target": "es2021", "lib": ["es2021"], "types": ["@cloudflare/workers-types"], - "resolveJsonModule": true, - "allowJs": false, - "allowSyntheticDefaultImports": true, - "moduleResolution": "Node16", - "module": "Node16" + "moduleResolution": "NodeNext", + "module": "NodeNext" } } diff --git a/packages/nexus/package.json b/packages/nexus/package.json index 7129dac..8f4eb27 100644 --- a/packages/nexus/package.json +++ b/packages/nexus/package.json @@ -43,7 +43,7 @@ "prepublish": "turbo build --filter=@whatsgood/nexus", "typecheck": "tsc", "lint": "eslint src/", - "lint:fix": "pnpm run lint --fix", + "lint:fix": "pnpm lint lint --fix", "test:unit": "vitest run --config ./vitest.config.ts", "test:watch": "vitest --config ./vitest.config.ts", "test:unit:coverage": "pnpm test:unit --coverage" From 11a8e01d38cf6df3d1dd70165b364e3c35c1317c Mon Sep 17 00:00:00 2001 From: Kerem Kazan Date: Mon, 13 Nov 2023 08:15:08 -0500 Subject: [PATCH 27/41] removed monorepo dev packages from cloudflare worker example --- examples/cloudflare-worker/package.json | 2 -- 1 file changed, 2 deletions(-) diff --git a/examples/cloudflare-worker/package.json b/examples/cloudflare-worker/package.json index 1570eaa..4d6837b 100644 --- a/examples/cloudflare-worker/package.json +++ b/examples/cloudflare-worker/package.json @@ -16,8 +16,6 @@ }, "devDependencies": { "@cloudflare/workers-types": "^4.20230419.0", - "eslint-config-custom": "workspace:*", - "tsconfig": "workspace:*", "tsup": "^6.1.3", "typescript": "5.2.2", "wrangler": "^3.0.0" From 57a8efd245c49d23a0c2a959f1035352383e48d7 Mon Sep 17 00:00:00 2001 From: Kerem Kazan Date: Mon, 13 Nov 2023 08:16:16 -0500 Subject: [PATCH 28/41] removed tsup from cf worker example --- examples/cloudflare-worker/package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/cloudflare-worker/package.json b/examples/cloudflare-worker/package.json index 4d6837b..b6a9ead 100644 --- a/examples/cloudflare-worker/package.json +++ b/examples/cloudflare-worker/package.json @@ -16,7 +16,6 @@ }, "devDependencies": { "@cloudflare/workers-types": "^4.20230419.0", - "tsup": "^6.1.3", "typescript": "5.2.2", "wrangler": "^3.0.0" } From 47fa06d928ee9efb9164f7ff8fa04cd366a49670 Mon Sep 17 00:00:00 2001 From: Kerem Kazan Date: Mon, 13 Nov 2023 08:23:32 -0500 Subject: [PATCH 29/41] docs:broken-links script added to check for broken links --- .github/workflows/ci.yml | 3 +++ .github/workflows/docs.yml | 2 +- docs/package.json | 3 ++- package.json | 3 ++- pnpm-lock.yaml | 6 ++++++ scripts/docs.sh | 4 ++-- turbo.json | 1 + 7 files changed, 17 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4e46684..00fb1ed 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -40,6 +40,9 @@ jobs: - name: Test run: pnpm test:unit + - name: Docs broken links + run: pnpm docs:broken-links + - name: Slack Notification id: slack uses: slackapi/slack-github-action@v1.24.0 diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index ed51fcb..ffd6c91 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -31,7 +31,7 @@ jobs: run: pnpm build - name: Docs - run: pnpm generate:docs --frozen + run: pnpm docs:generate --frozen - name: Slack Notification id: slack diff --git a/docs/package.json b/docs/package.json index f8aad09..f5f0be6 100644 --- a/docs/package.json +++ b/docs/package.json @@ -5,7 +5,8 @@ "main": "index.js", "private": true, "scripts": { - "docs:dev": "mintlify dev" + "docs:dev": "mintlify dev", + "docs:broken-links": "mintlify broken-links" }, "devDependencies": { "mintlify": "4.0.49" diff --git a/package.json b/package.json index 76423f0..d88bcf7 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,8 @@ "ci:publish": "./scripts/publish.sh", "ci:version": "./scripts/version.sh", "test:unit": "turbo test:unit", - "generate:docs": "./scripts/docs.sh" + "docs:generate": "./scripts/docs.sh", + "docs:broken-links": "turbo docs:broken-links" }, "devDependencies": { "@changesets/cli": "^2.26.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ae4d9a1..cff7c93 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -33,6 +33,12 @@ importers: specifier: 5.2.2 version: 5.2.2 + docs: + devDependencies: + mintlify: + specifier: 4.0.49 + version: 4.0.49 + examples/cloudflare-worker: dependencies: '@whatsgood/nexus': diff --git a/scripts/docs.sh b/scripts/docs.sh index deff88b..32340ed 100755 --- a/scripts/docs.sh +++ b/scripts/docs.sh @@ -39,13 +39,13 @@ sh ./scripts/generate-docs-changelog-mdx.sh if [ "$FROZEN" = true ] ; then echo "Checking if the changelog has changed" if [[ $(git diff --name-only) == *"docs/changelog.mdx"* ]] ; then - echo "Error: The changelog has changed. Please run 'pnpm generate:docs' and commit the changes" + echo "Error: The changelog has changed. Please run 'pnpm docs:generate' and commit the changes" exit 1 fi echo "Checking if the readme has changed" if [[ $(git diff --name-only) == *"packages/nexus/README.md"* ]] ; then - echo "Error: The README has changed. Please run 'pnpm generate:docs' and commit the changes" + echo "Error: The README has changed. Please run 'pnpm docs:generate' and commit the changes" exit 1 fi fi diff --git a/turbo.json b/turbo.json index 2c572b6..a3cf282 100644 --- a/turbo.json +++ b/turbo.json @@ -12,6 +12,7 @@ // TODO: see if we can actually cache the deploys }, "docs:dev": {}, + "docs:broken-links": {}, "test:unit": {}, "test:unit:coverage": { "outputs": ["coverage/**"] From 336cfe0d88a15a84fc5d33dda9d138fa1a5a8c86 Mon Sep 17 00:00:00 2001 From: Kerem Kazan Date: Mon, 13 Nov 2023 09:41:54 -0500 Subject: [PATCH 30/41] import uses esm modules now --- packages/nexus/package.json | 10 +++++----- packages/nexus/tsup.config.ts | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/nexus/package.json b/packages/nexus/package.json index 8f4eb27..ddba79e 100644 --- a/packages/nexus/package.json +++ b/packages/nexus/package.json @@ -22,23 +22,23 @@ "exports": { "./fetch": { "require": "./dist/fetch/index.js", - "import": "./dist/fetch/index.js", + "import": "./dist/fetch/index.mjs", "types": "./dist/fetch/index.d.ts" }, "./node": { "require": "./dist/node/index.js", - "import": "./dist/node/index.js", + "import": "./dist/node/index.mjs", "types": "./dist/node/index.d.ts" }, ".": { "require": "./dist/lib/index.js", - "import": "./dist/lib/index.js", + "import": "./dist/lib/index.mjs", "types": "./dist/lib/index.d.ts" - } + }, + "./package.json": "./package.json" }, "license": "MIT", "scripts": { - "dev": "echo 'Add dev script here'", "build": "tsup", "prepublish": "turbo build --filter=@whatsgood/nexus", "typecheck": "tsc", diff --git a/packages/nexus/tsup.config.ts b/packages/nexus/tsup.config.ts index 42c754b..f2c9621 100644 --- a/packages/nexus/tsup.config.ts +++ b/packages/nexus/tsup.config.ts @@ -4,9 +4,9 @@ import { defineConfig } from "tsup"; export default defineConfig((options: Options) => ({ treeshake: true, entry: ["./src/fetch/index.ts", "./src/node/index.ts", "./src/lib/index.ts"], - format: ["cjs"], + format: ["cjs", "esm"], dts: true, - minify: true, + minify: false, clean: true, sourcemap: true, splitting: true, From 422e402f4f788ee35c82ec8eda295f2b855d4e76 Mon Sep 17 00:00:00 2001 From: Kerem Kazan Date: Mon, 13 Nov 2023 10:22:54 -0500 Subject: [PATCH 31/41] deleted unintended js build from worker --- examples/cloudflare-worker/src/index.js | 16 ---------------- 1 file changed, 16 deletions(-) delete mode 100644 examples/cloudflare-worker/src/index.js diff --git a/examples/cloudflare-worker/src/index.js b/examples/cloudflare-worker/src/index.js deleted file mode 100644 index 28e5a8e..0000000 --- a/examples/cloudflare-worker/src/index.js +++ /dev/null @@ -1,16 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -const nexus_1 = require("@whatsgood/nexus"); -const fetch_1 = require("@whatsgood/nexus/fetch"); -// TODO: add config alerts to indicate that the key access is incomplete -// TODO: add onboarding & UX. (setup admin access, login, etc) -// TODO: add tests for the worker -exports.default = { - async fetch(request, env) { - const nexus = new nexus_1.Nexus({ - env, - }); - const requestHandler = new fetch_1.RequestHandler(nexus, request); - return requestHandler.handle(); - }, -}; From 861f716a071814d517742017771e9757b72731ad Mon Sep 17 00:00:00 2001 From: Kerem Kazan Date: Mon, 13 Nov 2023 11:34:50 -0500 Subject: [PATCH 32/41] cf worker doc uses same ts version as the rest of the monorepo --- docs/recipes/cloudflare-worker.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/recipes/cloudflare-worker.mdx b/docs/recipes/cloudflare-worker.mdx index d0ceedd..b1e8cdd 100644 --- a/docs/recipes/cloudflare-worker.mdx +++ b/docs/recipes/cloudflare-worker.mdx @@ -58,7 +58,7 @@ You can now `cd` into your new project directory and open it in your favorite ed }, "devDependencies": { "@cloudflare/workers-types": "^4.20230419.0", - "typescript": "^5.0.4", + "typescript": "5.2.2", "wrangler": "^3.0.0" } } @@ -302,7 +302,7 @@ export default { }, "devDependencies": { "@cloudflare/workers-types": "^4.20230419.0", - "typescript": "^5.0.4", + "typescript": "5.2.2", "wrangler": "^3.0.0" }, "dependencies": { From 8625031376b67c978907dd290dac9a9919b03b50 Mon Sep 17 00:00:00 2001 From: Kerem Kazan Date: Mon, 13 Nov 2023 11:37:12 -0500 Subject: [PATCH 33/41] added noEmit to cf worker build --- examples/cloudflare-worker/tsconfig.json | 3 +- pnpm-lock.yaml | 2173 +++++++++++++++++++++- 2 files changed, 2143 insertions(+), 33 deletions(-) diff --git a/examples/cloudflare-worker/tsconfig.json b/examples/cloudflare-worker/tsconfig.json index 70f1e6b..c4e191e 100644 --- a/examples/cloudflare-worker/tsconfig.json +++ b/examples/cloudflare-worker/tsconfig.json @@ -6,6 +6,7 @@ "lib": ["es2021"], "types": ["@cloudflare/workers-types"], "moduleResolution": "NodeNext", - "module": "NodeNext" + "module": "NodeNext", + "noEmit": true } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index cff7c93..cc77c99 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -48,15 +48,6 @@ importers: '@cloudflare/workers-types': specifier: ^4.20230419.0 version: 4.20230419.0 - eslint-config-custom: - specifier: workspace:* - version: link:../../packages/eslint-config-custom - tsconfig: - specifier: workspace:* - version: link:../../packages/tsconfig - tsup: - specifier: ^6.1.3 - version: 6.1.3(ts-node@10.9.1)(typescript@5.2.2) typescript: specifier: 5.2.2 version: 5.2.2 @@ -136,6 +127,38 @@ packages: '@jridgewell/trace-mapping': 0.3.19 dev: true + /@apidevtools/json-schema-ref-parser@9.0.6: + resolution: {integrity: sha512-M3YgsLjI0lZxvrpeGVk9Ap032W6TPQkH6pRAZz81Ac3WUNF79VQooAFnp8umjvVzUmD93NkogxEwbSce7qMsUg==} + dependencies: + '@jsdevtools/ono': 7.1.3 + call-me-maybe: 1.0.2 + js-yaml: 3.14.1 + dev: true + + /@apidevtools/openapi-schemas@2.1.0: + resolution: {integrity: sha512-Zc1AlqrJlX3SlpupFGpiLi2EbteyP7fXmUOGup6/DnkRgjP9bgMM/ag+n91rsv0U1Gpz0H3VILA/o3bW7Ua6BQ==} + engines: {node: '>=10'} + dev: true + + /@apidevtools/swagger-methods@3.0.2: + resolution: {integrity: sha512-QAkD5kK2b1WfjDS/UQn/qQkbwF31uqRjPTrsCs5ZG9BQGAkjwvqGFjjPqAuzac/IYzpPtRzjCP1WrTuAIjMrXg==} + dev: true + + /@apidevtools/swagger-parser@10.1.0(openapi-types@12.1.3): + resolution: {integrity: sha512-9Kt7EuS/7WbMAUv2gSziqjvxwDbFSg3Xeyfuj5laUODX8o/k/CpsAKiQ8W7/R88eXFTMbJYg6+7uAmOWNKmwnw==} + peerDependencies: + openapi-types: '>=7' + dependencies: + '@apidevtools/json-schema-ref-parser': 9.0.6 + '@apidevtools/openapi-schemas': 2.1.0 + '@apidevtools/swagger-methods': 3.0.2 + '@jsdevtools/ono': 7.1.3 + ajv: 8.12.0 + ajv-draft-04: 1.0.0(ajv@8.12.0) + call-me-maybe: 1.0.2 + openapi-types: 12.1.3 + dev: true + /@babel/code-frame@7.22.13: resolution: {integrity: sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==} engines: {node: '>=6.9.0'} @@ -1126,6 +1149,21 @@ packages: '@jridgewell/sourcemap-codec': 1.4.15 dev: true + /@jsdevtools/ono@7.1.3: + resolution: {integrity: sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==} + dev: true + + /@leichtgewicht/ip-codec@2.0.4: + resolution: {integrity: sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==} + dev: true + + /@ljharb/through@2.3.11: + resolution: {integrity: sha512-ccfcIDlogiXNq5KcbAwbaO7lMh3Tm1i3khMPYpxlK8hH/W53zN81KM9coerRLOnTGu3nfXIniAmQbRI9OxbC0w==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + dev: true + /@manypkg/find-root@1.1.0: resolution: {integrity: sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA==} dependencies: @@ -1159,6 +1197,162 @@ packages: resolution: {integrity: sha512-9b8mPpKrfeGRuhFH5iO1iwCLeIIsV6+H1sRfxbkoGXIyQE2BTsPd9zqSqQJ+pv5sJ/hT5M1zvOFL02MnEezFug==} dev: true + /@mintlify/cli@4.0.49: + resolution: {integrity: sha512-PxSE/3HIprv63Sf0635uBVX8++VnBFVy0EcGRslq9dvxIyDDx7RgaLIjo2969Zd6eowZTfzrQ9itpUx2cepJqw==} + engines: {node: '>=18.0.0'} + hasBin: true + dependencies: + '@apidevtools/swagger-parser': 10.1.0(openapi-types@12.1.3) + '@mintlify/link-rot': 3.0.71(@mintlify/models@0.0.53)(@mintlify/validation@0.1.96)(axios@1.6.1)(gray-matter@4.0.3) + '@mintlify/models': 0.0.53(axios@1.6.1)(openapi-types@12.1.3) + '@mintlify/prebuild': 1.0.71(@apidevtools/swagger-parser@10.1.0)(@mintlify/models@0.0.53)(@mintlify/validation@0.1.96)(axios@1.6.1)(fs-extra@11.1.1)(gray-matter@4.0.3)(openapi-types@12.1.3)(unist-util-visit@4.1.2) + '@mintlify/previewing': 4.0.48(@mintlify/models@0.0.53)(@mintlify/validation@0.1.96)(axios@1.6.1) + '@mintlify/validation': 0.1.96(@mintlify/models@0.0.53)(openapi-types@12.1.3) + axios: 1.6.1 + chalk: 5.3.0 + detect-port: 1.5.1 + favicons: 7.1.4 + fs-extra: 11.1.1 + gray-matter: 4.0.3 + inquirer: 9.2.12 + is-absolute-url: 4.0.1 + js-yaml: 4.1.0 + openapi-types: 12.1.3 + remark: 14.0.3 + remark-frontmatter: 4.0.1 + remark-gfm: 3.0.1 + remark-math: 5.1.1 + remark-mdx: 2.3.0 + unist-util-visit: 4.1.2 + vfile: 5.3.7 + yargs: 17.7.2 + transitivePeerDependencies: + - bufferutil + - debug + - encoding + - supports-color + - utf-8-validate + dev: true + + /@mintlify/common@1.0.3(axios@1.6.1)(openapi-types@12.1.3): + resolution: {integrity: sha512-WxLx9rqROFcSWdqgvvOADRnYtF24DY+nlTqW2rEqFptkcqiMyW7VghmvX9uQ+sYgPdOuXi6mawbZFltKbQfQyQ==} + dependencies: + '@mintlify/models': 0.0.53(axios@1.6.1)(openapi-types@12.1.3) + transitivePeerDependencies: + - axios + - openapi-types + dev: true + + /@mintlify/link-rot@3.0.71(@mintlify/models@0.0.53)(@mintlify/validation@0.1.96)(axios@1.6.1)(gray-matter@4.0.3): + resolution: {integrity: sha512-BtWZW1T3uqEd2uq6Fe3914X63PGr1yf/u02LLgik7GhHc54aJDSH+fhWBs18twhiRbmldBaA0QMjY6jgqVD/1Q==} + engines: {node: '>=18.0.0'} + dependencies: + '@apidevtools/swagger-parser': 10.1.0(openapi-types@12.1.3) + '@mintlify/prebuild': 1.0.71(@apidevtools/swagger-parser@10.1.0)(@mintlify/models@0.0.53)(@mintlify/validation@0.1.96)(axios@1.6.1)(fs-extra@11.1.1)(gray-matter@4.0.3)(openapi-types@12.1.3)(unist-util-visit@4.1.2) + chalk: 5.3.0 + fs-extra: 11.1.1 + is-absolute-url: 4.0.1 + openapi-types: 12.1.3 + remark: 14.0.3 + remark-frontmatter: 4.0.1 + remark-gfm: 3.0.1 + remark-math: 5.1.1 + remark-mdx: 2.3.0 + unist-util-visit: 4.1.2 + transitivePeerDependencies: + - '@mintlify/models' + - '@mintlify/validation' + - axios + - gray-matter + - supports-color + dev: true + + /@mintlify/models@0.0.53(axios@1.6.1)(openapi-types@12.1.3): + resolution: {integrity: sha512-VkekSMxT9nJyvIF9YJGG2RCrbeWLMr4FxE8sVOS0YGIcSjOoftr0zhro7dkkBCRDNIw0PR7dtDt1Z4GWtbAYyQ==} + engines: {node: '>=18.0.0'} + peerDependencies: + axios: ^1.4.0 + openapi-types: 12.x + dependencies: + axios: 1.6.1 + openapi-types: 12.1.3 + dev: true + + /@mintlify/prebuild@1.0.71(@apidevtools/swagger-parser@10.1.0)(@mintlify/models@0.0.53)(@mintlify/validation@0.1.96)(axios@1.6.1)(fs-extra@11.1.1)(gray-matter@4.0.3)(openapi-types@12.1.3)(unist-util-visit@4.1.2): + resolution: {integrity: sha512-0tl9EHQzpbVXbbwagiWrUsbnfb87DR1Tqz0DWhbjLynwGu53ul+I4Rap1Hxk34ag7FQ2aIxaUGvyMjSmcuAB9Q==} + peerDependencies: + '@apidevtools/swagger-parser': 10.x + '@mintlify/models': '>= 0.0.15 < 1' + '@mintlify/validation': '>= 0.1.57 < 1' + fs-extra: 11.x + gray-matter: ^4.0.3 + openapi-types: 12.x + unist-util-visit: ^4.1.1 + dependencies: + '@apidevtools/swagger-parser': 10.1.0(openapi-types@12.1.3) + '@mintlify/common': 1.0.3(axios@1.6.1)(openapi-types@12.1.3) + '@mintlify/models': 0.0.53(axios@1.6.1)(openapi-types@12.1.3) + '@mintlify/validation': 0.1.96(@mintlify/models@0.0.53)(openapi-types@12.1.3) + fs-extra: 11.1.1 + gray-matter: 4.0.3 + openapi-types: 12.1.3 + unist-util-visit: 4.1.2 + transitivePeerDependencies: + - axios + dev: true + + /@mintlify/previewing@4.0.48(@mintlify/models@0.0.53)(@mintlify/validation@0.1.96)(axios@1.6.1): + resolution: {integrity: sha512-QcbsraooOb9umXWiJdJfKlFDf1qNMcYtsXj/8oZ6/deFFmPvqfkHq8RtQm8JiAY5L2hWfvnf5uLdd6i5Ug1p7Q==} + engines: {node: '>=18.0.0'} + peerDependencies: + '@mintlify/validation': '>= 0.1.48 < 1' + dependencies: + '@apidevtools/swagger-parser': 10.1.0(openapi-types@12.1.3) + '@mintlify/prebuild': 1.0.71(@apidevtools/swagger-parser@10.1.0)(@mintlify/models@0.0.53)(@mintlify/validation@0.1.96)(axios@1.6.1)(fs-extra@11.1.1)(gray-matter@4.0.3)(openapi-types@12.1.3)(unist-util-visit@4.1.2) + '@mintlify/validation': 0.1.96(@mintlify/models@0.0.53)(openapi-types@12.1.3) + '@octokit/rest': 19.0.13 + chalk: 5.3.0 + chokidar: 3.5.3 + express: 4.18.2 + fs-extra: 11.1.1 + got: 13.0.0 + gray-matter: 4.0.3 + is-absolute-url: 4.0.1 + is-online: 10.0.0 + open: 8.4.2 + openapi-types: 12.1.3 + ora: 6.3.1 + remark: 14.0.3 + remark-frontmatter: 4.0.1 + remark-gfm: 3.0.1 + remark-math: 5.1.1 + remark-mdx: 2.3.0 + socket.io: 4.7.2 + tar: 6.2.0 + unist-util-visit: 4.1.2 + yargs: 17.7.2 + transitivePeerDependencies: + - '@mintlify/models' + - axios + - bufferutil + - encoding + - supports-color + - utf-8-validate + dev: true + + /@mintlify/validation@0.1.96(@mintlify/models@0.0.53)(openapi-types@12.1.3): + resolution: {integrity: sha512-iDdUbNa3B/Ebro6WwL1osTJWWR4HI15xTUq36jLHrLzKHleXttkmYo4FcgyqOjFBn8tRyMlrqM+Uj1WmN+IJHA==} + peerDependencies: + '@mintlify/models': '>= 0.0.36 < 1' + openapi-types: 12.x + dependencies: + '@mintlify/models': 0.0.53(axios@1.6.1)(openapi-types@12.1.3) + lcm: 0.0.3 + openapi-types: 12.1.3 + zod: 3.22.2 + zod-to-json-schema: 3.21.4(zod@3.22.2) + dev: true + /@mswjs/cookies@0.2.2: resolution: {integrity: sha512-mlN83YSrcFgk7Dm1Mys40DLssI1KdJji2CMKN8eOlBqsTADYzj2+jWzsANsUTFbxDMWPD5e9bfA1RGqBpS3O1g==} engines: {node: '>=14'} @@ -1210,6 +1404,130 @@ packages: fastq: 1.15.0 dev: true + /@octokit/auth-token@3.0.4: + resolution: {integrity: sha512-TWFX7cZF2LXoCvdmJWY7XVPi74aSY0+FfBZNSXEXFkMpjcqsQwDSYVv5FhRFaI0V1ECnwbz4j59T/G+rXNWaIQ==} + engines: {node: '>= 14'} + dev: true + + /@octokit/core@4.2.4: + resolution: {integrity: sha512-rYKilwgzQ7/imScn3M9/pFfUf4I1AZEH3KhyJmtPdE2zfaXAn2mFfUy4FbKewzc2We5y/LlKLj36fWJLKC2SIQ==} + engines: {node: '>= 14'} + dependencies: + '@octokit/auth-token': 3.0.4 + '@octokit/graphql': 5.0.6 + '@octokit/request': 6.2.8 + '@octokit/request-error': 3.0.3 + '@octokit/types': 9.3.2 + before-after-hook: 2.2.3 + universal-user-agent: 6.0.1 + transitivePeerDependencies: + - encoding + dev: true + + /@octokit/endpoint@7.0.6: + resolution: {integrity: sha512-5L4fseVRUsDFGR00tMWD/Trdeeihn999rTMGRMC1G/Ldi1uWlWJzI98H4Iak5DB/RVvQuyMYKqSK/R6mbSOQyg==} + engines: {node: '>= 14'} + dependencies: + '@octokit/types': 9.3.2 + is-plain-object: 5.0.0 + universal-user-agent: 6.0.1 + dev: true + + /@octokit/graphql@5.0.6: + resolution: {integrity: sha512-Fxyxdy/JH0MnIB5h+UQ3yCoh1FG4kWXfFKkpWqjZHw/p+Kc8Y44Hu/kCgNBT6nU1shNumEchmW/sUO1JuQnPcw==} + engines: {node: '>= 14'} + dependencies: + '@octokit/request': 6.2.8 + '@octokit/types': 9.3.2 + universal-user-agent: 6.0.1 + transitivePeerDependencies: + - encoding + dev: true + + /@octokit/openapi-types@18.1.1: + resolution: {integrity: sha512-VRaeH8nCDtF5aXWnjPuEMIYf1itK/s3JYyJcWFJT8X9pSNnBtriDf7wlEWsGuhPLl4QIH4xM8fqTXDwJ3Mu6sw==} + dev: true + + /@octokit/plugin-paginate-rest@6.1.2(@octokit/core@4.2.4): + resolution: {integrity: sha512-qhrmtQeHU/IivxucOV1bbI/xZyC/iOBhclokv7Sut5vnejAIAEXVcGQeRpQlU39E0WwK9lNvJHphHri/DB6lbQ==} + engines: {node: '>= 14'} + peerDependencies: + '@octokit/core': '>=4' + dependencies: + '@octokit/core': 4.2.4 + '@octokit/tsconfig': 1.0.2 + '@octokit/types': 9.3.2 + dev: true + + /@octokit/plugin-request-log@1.0.4(@octokit/core@4.2.4): + resolution: {integrity: sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA==} + peerDependencies: + '@octokit/core': '>=3' + dependencies: + '@octokit/core': 4.2.4 + dev: true + + /@octokit/plugin-rest-endpoint-methods@7.2.3(@octokit/core@4.2.4): + resolution: {integrity: sha512-I5Gml6kTAkzVlN7KCtjOM+Ruwe/rQppp0QU372K1GP7kNOYEKe8Xn5BW4sE62JAHdwpq95OQK/qGNyKQMUzVgA==} + engines: {node: '>= 14'} + peerDependencies: + '@octokit/core': '>=3' + dependencies: + '@octokit/core': 4.2.4 + '@octokit/types': 10.0.0 + dev: true + + /@octokit/request-error@3.0.3: + resolution: {integrity: sha512-crqw3V5Iy2uOU5Np+8M/YexTlT8zxCfI+qu+LxUB7SZpje4Qmx3mub5DfEKSO8Ylyk0aogi6TYdf6kxzh2BguQ==} + engines: {node: '>= 14'} + dependencies: + '@octokit/types': 9.3.2 + deprecation: 2.3.1 + once: 1.4.0 + dev: true + + /@octokit/request@6.2.8: + resolution: {integrity: sha512-ow4+pkVQ+6XVVsekSYBzJC0VTVvh/FCTUUgTsboGq+DTeWdyIFV8WSCdo0RIxk6wSkBTHqIK1mYuY7nOBXOchw==} + engines: {node: '>= 14'} + dependencies: + '@octokit/endpoint': 7.0.6 + '@octokit/request-error': 3.0.3 + '@octokit/types': 9.3.2 + is-plain-object: 5.0.0 + node-fetch: 2.7.0 + universal-user-agent: 6.0.1 + transitivePeerDependencies: + - encoding + dev: true + + /@octokit/rest@19.0.13: + resolution: {integrity: sha512-/EzVox5V9gYGdbAI+ovYj3nXQT1TtTHRT+0eZPcuC05UFSWO3mdO9UY1C0i2eLF9Un1ONJkAk+IEtYGAC+TahA==} + engines: {node: '>= 14'} + dependencies: + '@octokit/core': 4.2.4 + '@octokit/plugin-paginate-rest': 6.1.2(@octokit/core@4.2.4) + '@octokit/plugin-request-log': 1.0.4(@octokit/core@4.2.4) + '@octokit/plugin-rest-endpoint-methods': 7.2.3(@octokit/core@4.2.4) + transitivePeerDependencies: + - encoding + dev: true + + /@octokit/tsconfig@1.0.2: + resolution: {integrity: sha512-I0vDR0rdtP8p2lGMzvsJzbhdOWy405HcGovrspJ8RRibHnyRgggUSNO5AIox5LmqiwmatHKYsvj6VGFHkqS7lA==} + dev: true + + /@octokit/types@10.0.0: + resolution: {integrity: sha512-Vm8IddVmhCgU1fxC1eyinpwqzXPEYu0NrYzD3YZjlGjyftdLBTeqNblRC0jmJmgxbJIsQlyogVeGnrNaaMVzIg==} + dependencies: + '@octokit/openapi-types': 18.1.1 + dev: true + + /@octokit/types@9.3.2: + resolution: {integrity: sha512-D4iHGTdAnEEVsB8fl95m1hiz7D5YiRdQ9b/OEb3BYRVwbLsGHcRVPz+u+BgRLNk0Q0/4iZCBqDN96j2XNxfXrA==} + dependencies: + '@octokit/openapi-types': 18.1.1 + dev: true + /@open-draft/until@1.0.3: resolution: {integrity: sha512-Aq58f5HiWdyDlFffbbSjAlv596h/cOnt2DO1w3DOC7OJ5EHs0hd/nycJfiu9RJbT6Yk6F1knnRRXNSpxoIVZ9Q==} dev: true @@ -1233,6 +1551,22 @@ packages: /@sinclair/typebox@0.27.8: resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} + /@sindresorhus/is@5.6.0: + resolution: {integrity: sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g==} + engines: {node: '>=14.16'} + dev: true + + /@socket.io/component-emitter@3.1.0: + resolution: {integrity: sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==} + dev: true + + /@szmarczak/http-timer@5.0.1: + resolution: {integrity: sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==} + engines: {node: '>=14.16'} + dependencies: + defer-to-connect: 2.0.1 + dev: true + /@tootallnate/quickjs-emscripten@0.23.0: resolution: {integrity: sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==} dev: true @@ -1294,6 +1628,12 @@ packages: update-check: 1.5.4 dev: true + /@types/acorn@4.0.6: + resolution: {integrity: sha512-veQTnWP+1D/xbxVrPC3zHnCZRjSrKfhbMUlEA43iMZLu7EsnTtkJklIuwrCPbOi8YkvDQAiW05VQQFvvz9oieQ==} + dependencies: + '@types/estree': 1.0.5 + dev: true + /@types/body-parser@1.19.3: resolution: {integrity: sha512-oyl4jvAfTGX9Bt6Or4H9ni1Z447/tQuxnZsytsCaExKlmJiU8sFgnIBRzJUpKwB5eWn9HuBYlUlVA74q/yN0eQ==} dependencies: @@ -1319,12 +1659,28 @@ packages: resolution: {integrity: sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==} dev: true + /@types/cors@2.8.16: + resolution: {integrity: sha512-Trx5or1Nyg1Fq138PCuWqoApzvoSLWzZ25ORBiHMbbUT42g578lH1GT4TwYDbiUOLFuDsCkfLneT2105fsFWGg==} + dependencies: + '@types/node': 18.18.0 + dev: true + /@types/debug@4.1.9: resolution: {integrity: sha512-8Hz50m2eoS56ldRlepxSBa6PWEVCtzUo/92HgLc2qTMnotJNIm7xP+UZhyWoYsyOdd5dxZ+NZLb24rsKyFs2ow==} dependencies: '@types/ms': 0.7.32 dev: true + /@types/estree-jsx@1.0.3: + resolution: {integrity: sha512-pvQ+TKeRHeiUGRhvYwRrQ/ISnohKkSJR14fT2yqyZ4e9K5vqc7hrtY2Y1Dw0ZwAzQ6DQsxsaCUuSIIi8v0Cq6w==} + dependencies: + '@types/estree': 1.0.5 + dev: true + + /@types/estree@1.0.5: + resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} + dev: true + /@types/express-serve-static-core@4.17.37: resolution: {integrity: sha512-ZohaCYTgGFcOP7u6aJOhY9uIZQgZ2vxC2yWoArY+FeDXlqeH66ZVBjgvg+RLVAS/DWNq4Ap9ZXu1+SUQiiWYMg==} dependencies: @@ -1350,6 +1706,16 @@ packages: '@types/node': 18.18.0 dev: true + /@types/hast@2.3.8: + resolution: {integrity: sha512-aMIqAlFd2wTIDZuvLbhUT+TGvMxrNC8ECUIVtH6xxy0sQLs3iu6NO8Kp/VT5je7i5ufnebXzdV1dNDMnvaH6IQ==} + dependencies: + '@types/unist': 2.0.10 + dev: true + + /@types/http-cache-semantics@4.0.4: + resolution: {integrity: sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==} + dev: true + /@types/http-errors@2.0.2: resolution: {integrity: sha512-lPG6KlZs88gef6aD85z3HNkztpj7w2R7HmR3gygjfXCQmsLloWNARFkMuzKiiY8FGdh1XDpgBdrSf4aKDiA7Kg==} dev: true @@ -1383,6 +1749,16 @@ packages: resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} dev: true + /@types/katex@0.16.6: + resolution: {integrity: sha512-rZYO1HInM99rAFYNwGqbYPxHZHxu2IwZYKj4bJ4oh6edVrm1UId8mmbHIZLBtG253qU6y3piag0XYe/joNnwzQ==} + dev: true + + /@types/mdast@3.0.15: + resolution: {integrity: sha512-LnwD+mUEfxWMa1QpDraczIn6k0Ee3SMicuYSSzS6ZYl2gKS09EClnJYGd8Du6rfc5r/GZEk5o1mRb8TaTj03sQ==} + dependencies: + '@types/unist': 2.0.10 + dev: true + /@types/mime@1.3.3: resolution: {integrity: sha512-Ys+/St+2VF4+xuY6+kDIXGxbNRO0mesVg0bbxEfB97Od1Vjpjx9KD1qxs64Gcb3CWPirk9Xe+PT4YiiHQ9T+eg==} dev: true @@ -1457,6 +1833,10 @@ packages: resolution: {integrity: sha512-FYK4mlLxUUajo/mblv7EUDHku20qT6ThYNsGZsTHilcHRvIkF8WXqtZO+DVTYkpHWCaAT97LueV59H/5Ve3bGA==} dev: true + /@types/unist@2.0.10: + resolution: {integrity: sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA==} + dev: true + /@typescript-eslint/eslint-plugin@6.7.3(@typescript-eslint/parser@6.7.3)(eslint@8.50.0)(typescript@5.2.2): resolution: {integrity: sha512-vntq452UHNltxsaaN+L9WyuMch8bMd9CqJ3zhzTPXXidwbf5mqqKCVXEuvRZUqLJSTLeWE65lQwyXsRGnXkCTA==} engines: {node: ^16.0.0 || >=18.0.0} @@ -1787,6 +2167,11 @@ packages: engines: {node: '>=0.4.0'} hasBin: true + /address@1.2.2: + resolution: {integrity: sha512-4B/qKCfeE/ODUaAUpSwfzazo5x29WD4r3vXiWsB7I2mSDAihwEqKO+g8GELZUQSSAo5e1XTYh3ZVfLyxBc12nA==} + engines: {node: '>= 10.0.0'} + dev: true + /agent-base@7.1.0: resolution: {integrity: sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==} engines: {node: '>= 14'} @@ -1804,6 +2189,25 @@ packages: indent-string: 4.0.0 dev: true + /aggregate-error@4.0.1: + resolution: {integrity: sha512-0poP0T7el6Vq3rstR8Mn4V/IQrpBLO6POkUSrN7RhyY+GF/InCFShQzsQ39T25gkHhLgSLByyAz+Kjb+c2L98w==} + engines: {node: '>=12'} + dependencies: + clean-stack: 4.2.0 + indent-string: 5.0.0 + dev: true + + /ajv-draft-04@1.0.0(ajv@8.12.0): + resolution: {integrity: sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==} + peerDependencies: + ajv: ^8.5.0 + peerDependenciesMeta: + ajv: + optional: true + dependencies: + ajv: 8.12.0 + dev: true + /ajv@6.12.6: resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} dependencies: @@ -1813,6 +2217,15 @@ packages: uri-js: 4.4.1 dev: true + /ajv@8.12.0: + resolution: {integrity: sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==} + dependencies: + fast-deep-equal: 3.1.3 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + uri-js: 4.4.1 + dev: true + /ansi-colors@4.1.3: resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} engines: {node: '>=6'} @@ -1830,6 +2243,11 @@ packages: engines: {node: '>=8'} dev: true + /ansi-regex@6.0.1: + resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} + engines: {node: '>=12'} + dev: true + /ansi-styles@3.2.1: resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} engines: {node: '>=4'} @@ -1992,6 +2410,10 @@ packages: has-symbols: 1.0.3 dev: true + /asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + dev: true + /available-typed-arrays@1.0.5: resolution: {integrity: sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==} engines: {node: '>= 0.4'} @@ -2002,12 +2424,30 @@ packages: engines: {node: '>=4'} dev: true + /axios@1.6.1: + resolution: {integrity: sha512-vfBmhDpKafglh0EldBEbVuoe7DyAavGSLWhuSm5ZSEKQnHhBf0xAAwybbNH1IkrJNGnS/VG4I5yxig1pCEXE4g==} + dependencies: + follow-redirects: 1.15.3 + form-data: 4.0.0 + proxy-from-env: 1.1.0 + transitivePeerDependencies: + - debug + dev: true + /axobject-query@3.2.1: resolution: {integrity: sha512-jsyHu61e6N4Vbz/v18DHwWYKK0bSWLqn47eeDSKPB7m8tqMHF9YJ+mhIk2lVteyZrY8tnSj/jHOv4YiTCuCJgg==} dependencies: dequal: 2.0.3 dev: true + /b4a@1.6.4: + resolution: {integrity: sha512-fpWrvyVHEKyeEvbKZTVOeZF3VSKKWtJxFIxX/jaVPf+cLbGUSitjb49pHLqPV2BUNNZ0LcoeEGfE/YCpyDYHIw==} + dev: true + + /bail@2.0.2: + resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==} + dev: true + /balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} dev: true @@ -2016,11 +2456,20 @@ packages: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} dev: true + /base64id@2.0.0: + resolution: {integrity: sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==} + engines: {node: ^4.5.0 || >= 5.9} + dev: true + /basic-ftp@5.0.3: resolution: {integrity: sha512-QHX8HLlncOLpy54mh+k/sWIFd0ThmRqwe9ZjELybGZK+tZ8rUb9VO0saKJUROTbE+KhzDUT7xziGpGrW8Kmd+g==} engines: {node: '>=10.0.0'} dev: true + /before-after-hook@2.2.3: + resolution: {integrity: sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==} + dev: true + /better-path-resolve@1.0.0: resolution: {integrity: sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==} engines: {node: '>=4'} @@ -2046,6 +2495,14 @@ packages: readable-stream: 3.6.2 dev: true + /bl@5.1.0: + resolution: {integrity: sha512-tv1ZJHLfTDnXE6tMHv73YgSJaWR2AFuPwMntBe7XL/GBFHnT0CLnsHMogfk5+GzCDC5ZWarSCYaIGATZt9dNsQ==} + dependencies: + buffer: 6.0.3 + inherits: 2.0.4 + readable-stream: 3.6.2 + dev: true + /blake3-wasm@2.1.5: resolution: {integrity: sha512-F1+K8EbfOZE49dtoPtmxUQrpXaBIl3ICvasLh+nJta0xkz+9kF/7uet9fLnwKqhDrmj6g+6K3Tw9yQPUg2ka5g==} dev: true @@ -2125,6 +2582,13 @@ packages: ieee754: 1.2.1 dev: true + /buffer@6.0.3: + resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + dev: true + /builtin-modules@3.3.0: resolution: {integrity: sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==} engines: {node: '>=6'} @@ -2169,6 +2633,24 @@ packages: resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} engines: {node: '>=8'} + /cacheable-lookup@7.0.0: + resolution: {integrity: sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==} + engines: {node: '>=14.16'} + dev: true + + /cacheable-request@10.2.14: + resolution: {integrity: sha512-zkDT5WAF4hSSoUgyfg5tFIxz8XQK+25W/TLVojJTMKBaxevLBBtLxgqguAuVQB8PVW79FVjHcU+GJ9tVbDZ9mQ==} + engines: {node: '>=14.16'} + dependencies: + '@types/http-cache-semantics': 4.0.4 + get-stream: 6.0.1 + http-cache-semantics: 4.1.1 + keyv: 4.5.3 + mimic-response: 4.0.0 + normalize-url: 8.0.0 + responselike: 3.0.0 + dev: true + /call-bind@1.0.2: resolution: {integrity: sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==} dependencies: @@ -2176,6 +2658,10 @@ packages: get-intrinsic: 1.2.1 dev: true + /call-me-maybe@1.0.2: + resolution: {integrity: sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==} + dev: true + /callsites@3.1.0: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} @@ -2215,6 +2701,10 @@ packages: - supports-color dev: true + /ccount@2.0.1: + resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} + dev: true + /chai@4.3.10: resolution: {integrity: sha512-0UXG04VuVbruMUYbJ6JctvH0YnC/4q3/AkT18q4NaITo91CUm0liMS9VqzT9vZhVQ/1eqPanMWjBM+Juhfb/9g==} engines: {node: '>=4'} @@ -2252,6 +2742,11 @@ packages: supports-color: 7.2.0 dev: true + /chalk@5.3.0: + resolution: {integrity: sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + dev: true + /change-case@3.1.0: resolution: {integrity: sha512-2AZp7uJZbYEzRPsFoa+ijKdvp9zsrnnt6+yFokfwEpeJm0xuJDVoxiRCAaTzyJND8GJkofo2IcKWaUZ/OECVzw==} dependencies: @@ -2275,6 +2770,22 @@ packages: upper-case-first: 1.1.2 dev: true + /character-entities-html4@2.1.0: + resolution: {integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==} + dev: true + + /character-entities-legacy@3.0.0: + resolution: {integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==} + dev: true + + /character-entities@2.0.2: + resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==} + dev: true + + /character-reference-invalid@2.0.1: + resolution: {integrity: sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==} + dev: true + /chardet@0.7.0: resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} dev: true @@ -2299,6 +2810,15 @@ packages: fsevents: 2.3.3 dev: true + /chownr@1.1.4: + resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==} + dev: true + + /chownr@2.0.0: + resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} + engines: {node: '>=10'} + dev: true + /ci-info@3.8.0: resolution: {integrity: sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==} engines: {node: '>=8'} @@ -2316,6 +2836,13 @@ packages: engines: {node: '>=6'} dev: true + /clean-stack@4.2.0: + resolution: {integrity: sha512-LYv6XPxoyODi36Dp976riBtSY27VmFo+MKqEU9QCCWyTrdEPDog+RWA7xQWHi6Vbp61j5c4cdzzX1NidnwtUWg==} + engines: {node: '>=12'} + dependencies: + escape-string-regexp: 5.0.0 + dev: true + /cli-cursor@3.1.0: resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} engines: {node: '>=8'} @@ -2323,6 +2850,13 @@ packages: restore-cursor: 3.1.0 dev: true + /cli-cursor@4.0.0: + resolution: {integrity: sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + restore-cursor: 4.0.0 + dev: true + /cli-spinners@2.9.1: resolution: {integrity: sha512-jHgecW0pxkonBJdrKsqxgRX9AcG+u/5k0Q7WPDfi8AogLAdwxEkyYYNWwZ5GvVFoFx2uiY1eNcSK00fh+1+FyQ==} engines: {node: '>=6'} @@ -2333,6 +2867,11 @@ packages: engines: {node: '>= 10'} dev: true + /cli-width@4.1.0: + resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==} + engines: {node: '>= 12'} + dev: true + /cliui@6.0.0: resolution: {integrity: sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==} dependencies: @@ -2376,14 +2915,41 @@ packages: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} dev: true - /commander@10.0.1: - resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==} - engines: {node: '>=14'} + /color-string@1.9.1: + resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==} + dependencies: + color-name: 1.1.4 + simple-swizzle: 0.2.2 dev: true - /commander@4.1.1: - resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} - engines: {node: '>= 6'} + /color@4.2.3: + resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==} + engines: {node: '>=12.5.0'} + dependencies: + color-convert: 2.0.1 + color-string: 1.9.1 + dev: true + + /combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + dependencies: + delayed-stream: 1.0.0 + dev: true + + /commander@10.0.1: + resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==} + engines: {node: '>=14'} + dev: true + + /commander@4.1.1: + resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} + engines: {node: '>= 6'} + dev: true + + /commander@8.3.0: + resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==} + engines: {node: '>= 12'} dev: true /concat-map@0.0.1: @@ -2436,6 +3002,14 @@ packages: requiresBuild: true dev: true + /cors@2.8.5: + resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==} + engines: {node: '>= 0.10'} + dependencies: + object-assign: 4.1.1 + vary: 1.1.2 + dev: true + /create-require@1.1.1: resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} dev: true @@ -2538,6 +3112,19 @@ packages: engines: {node: '>=0.10.0'} dev: true + /decode-named-character-reference@1.0.2: + resolution: {integrity: sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==} + dependencies: + character-entities: 2.0.2 + dev: true + + /decompress-response@6.0.0: + resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} + engines: {node: '>=10'} + dependencies: + mimic-response: 3.1.0 + dev: true + /deep-eql@4.1.3: resolution: {integrity: sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==} engines: {node: '>=6'} @@ -2577,6 +3164,11 @@ packages: clone: 1.0.4 dev: true + /defer-to-connect@2.0.1: + resolution: {integrity: sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==} + engines: {node: '>=10'} + dev: true + /define-data-property@1.1.0: resolution: {integrity: sha512-UzGwzcjyv3OtAvolTj1GoyNYzfFR+iqbGjcnBEENZVCpM4/Ng1yhGNvS3lR/xDS74Tb2wGG9WzNSNIOS9UVb2g==} engines: {node: '>= 0.4'} @@ -2586,6 +3178,11 @@ packages: has-property-descriptors: 1.0.0 dev: true + /define-lazy-prop@2.0.0: + resolution: {integrity: sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==} + engines: {node: '>=8'} + dev: true + /define-lazy-prop@3.0.0: resolution: {integrity: sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==} engines: {node: '>=12'} @@ -2623,11 +3220,20 @@ packages: slash: 3.0.0 dev: true + /delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + dev: true + /depd@2.0.0: resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} engines: {node: '>= 0.8'} dev: true + /deprecation@2.3.1: + resolution: {integrity: sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==} + dev: true + /dequal@2.0.3: resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} engines: {node: '>=6'} @@ -2648,11 +3254,26 @@ packages: engines: {node: '>=12.20'} dev: true + /detect-libc@2.0.2: + resolution: {integrity: sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==} + engines: {node: '>=8'} + dev: true + /detect-newline@4.0.1: resolution: {integrity: sha512-qE3Veg1YXzGHQhlA6jzebZN2qVf6NX+A7m7qlhCGG30dJixrAQhYOsJjsnBjJkCSmuOPpCk30145fr8FV0bzog==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} dev: true + /detect-port@1.5.1: + resolution: {integrity: sha512-aBzdj76lueB6uUst5iAs7+0H/oOjqI5D16XUWxlWMIMROhcM0rfsNVk93zTngq1dDNpoXRr++Sus7ETAExppAQ==} + hasBin: true + dependencies: + address: 1.2.2 + debug: 4.3.4 + transitivePeerDependencies: + - supports-color + dev: true + /diff-sequences@29.6.3: resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -2662,6 +3283,11 @@ packages: engines: {node: '>=0.3.1'} dev: true + /diff@5.1.0: + resolution: {integrity: sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==} + engines: {node: '>=0.3.1'} + dev: true + /dir-glob@3.0.1: resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} engines: {node: '>=8'} @@ -2669,6 +3295,20 @@ packages: path-type: 4.0.0 dev: true + /dns-packet@5.6.1: + resolution: {integrity: sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==} + engines: {node: '>=6'} + dependencies: + '@leichtgewicht/ip-codec': 2.0.4 + dev: true + + /dns-socket@4.2.2: + resolution: {integrity: sha512-BDeBd8najI4/lS00HSKpdFia+OvUMytaVjfzR9n5Lq8MlZRSvtbI+uLtx1+XmQFls5wFU9dssccTmQQ6nfpjdg==} + engines: {node: '>=6'} + dependencies: + dns-packet: 5.6.1 + dev: true + /doctrine@2.1.0: resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} engines: {node: '>=0.10.0'} @@ -2715,6 +3355,37 @@ packages: engines: {node: '>= 0.8'} dev: true + /end-of-stream@1.4.4: + resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} + dependencies: + once: 1.4.0 + dev: true + + /engine.io-parser@5.2.1: + resolution: {integrity: sha512-9JktcM3u18nU9N2Lz3bWeBgxVgOKpw7yhRaoxQA3FUDZzzw+9WlA6p4G4u0RixNkg14fH7EfEc/RhpurtiROTQ==} + engines: {node: '>=10.0.0'} + dev: true + + /engine.io@6.5.4: + resolution: {integrity: sha512-KdVSDKhVKyOi+r5uEabrDLZw2qXStVvCsEB/LN3mw4WFi6Gx50jTyuxYVCwAAC0U46FdnzP/ScKRBTXb/NiEOg==} + engines: {node: '>=10.2.0'} + dependencies: + '@types/cookie': 0.4.1 + '@types/cors': 2.8.16 + '@types/node': 18.18.0 + accepts: 1.3.8 + base64id: 2.0.0 + cookie: 0.4.2 + cors: 2.8.5 + debug: 4.3.4 + engine.io-parser: 5.2.1 + ws: 8.11.0 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + dev: true + /enhanced-resolve@5.15.0: resolution: {integrity: sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg==} engines: {node: '>=10.13.0'} @@ -3112,6 +3783,11 @@ packages: engines: {node: '>=10'} dev: true + /escape-string-regexp@5.0.0: + resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} + engines: {node: '>=12'} + dev: true + /escodegen@2.1.0: resolution: {integrity: sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==} engines: {node: '>=6.0'} @@ -3568,6 +4244,17 @@ packages: engines: {node: '>=4.0'} dev: true + /estree-util-is-identifier-name@2.1.0: + resolution: {integrity: sha512-bEN9VHRyXAUOjkKVQVvArFym08BTWB0aJPppZZr0UNyAqWsLaVfAqP7hbaTJjzHifmB5ebnR8Wm7r7yGN/HonQ==} + dev: true + + /estree-util-visit@1.2.1: + resolution: {integrity: sha512-xbgqcrkIVbIG+lI/gzbvd9SGTJL4zqJKBFttUl5pP27KhAjtMKbX/mQXJ7qgyXpMgVy/zvpm0xoQQaGL8OloOw==} + dependencies: + '@types/estree-jsx': 1.0.3 + '@types/unist': 2.0.10 + dev: true + /estree-walker@0.6.1: resolution: {integrity: sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==} dev: true @@ -3622,6 +4309,11 @@ packages: engines: {node: '>=6'} dev: true + /expand-template@2.0.3: + resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==} + engines: {node: '>=6'} + dev: true + /express@4.18.2: resolution: {integrity: sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==} engines: {node: '>= 0.10.0'} @@ -3661,6 +4353,17 @@ packages: - supports-color dev: true + /extend-shallow@2.0.1: + resolution: {integrity: sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==} + engines: {node: '>=0.10.0'} + dependencies: + is-extendable: 0.1.1 + dev: true + + /extend@3.0.2: + resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} + dev: true + /extendable-error@0.1.7: resolution: {integrity: sha512-UOiS2in6/Q0FK0R0q6UY9vYpQ21mr/Qn1KOnte7vsACuNJf514WvCCUHSRCPcgjPT2bAhNIJdlE6bVap1GKmeg==} dev: true @@ -3678,6 +4381,10 @@ packages: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} dev: true + /fast-fifo@1.3.2: + resolution: {integrity: sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==} + dev: true + /fast-glob@3.3.1: resolution: {integrity: sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==} engines: {node: '>=8.6.0'} @@ -3703,6 +4410,21 @@ packages: reusify: 1.0.4 dev: true + /fault@2.0.1: + resolution: {integrity: sha512-WtySTkS4OKev5JtpHXnib4Gxiurzh5NCGvWrFaZ34m6JehfTUhKZvn9njTfw48t6JumVQOmrKqpmGcdwxnhqBQ==} + dependencies: + format: 0.2.2 + dev: true + + /favicons@7.1.4: + resolution: {integrity: sha512-lnZpVgT7Fzz+DUjioKF1dMwLYlpqWCaB4gIksIfIKwtlhHO1Q7w23hERwHQjEsec+43iENwbTAPRDW3XvpLhbg==} + engines: {node: '>=14.0.0'} + dependencies: + escape-html: 1.0.3 + sharp: 0.32.6 + xml2js: 0.6.2 + dev: true + /figures@3.2.0: resolution: {integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==} engines: {node: '>=8'} @@ -3710,6 +4432,14 @@ packages: escape-string-regexp: 1.0.5 dev: true + /figures@5.0.0: + resolution: {integrity: sha512-ej8ksPF4x6e5wvK9yevct0UCXh8TTFlWGVLlgjZuoBH1HwjIfKE/IdL5mq89sFA7zELi1VhKpmtDnrs7zWyeyg==} + engines: {node: '>=14'} + dependencies: + escape-string-regexp: 5.0.0 + is-unicode-supported: 1.3.0 + dev: true + /file-entry-cache@6.0.1: resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} engines: {node: ^10.12.0 || >=12.0.0} @@ -3775,12 +4505,41 @@ packages: resolution: {integrity: sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==} dev: true + /follow-redirects@1.15.3: + resolution: {integrity: sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + dev: true + /for-each@0.3.3: resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} dependencies: is-callable: 1.2.7 dev: true + /form-data-encoder@2.1.4: + resolution: {integrity: sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw==} + engines: {node: '>= 14.17'} + dev: true + + /form-data@4.0.0: + resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==} + engines: {node: '>= 6'} + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + mime-types: 2.1.35 + dev: true + + /format@0.2.2: + resolution: {integrity: sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==} + engines: {node: '>=0.4.x'} + dev: true + /forwarded@0.2.0: resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} engines: {node: '>= 0.6'} @@ -3791,6 +4550,10 @@ packages: engines: {node: '>= 0.6'} dev: true + /fs-constants@1.0.0: + resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} + dev: true + /fs-extra@10.1.0: resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} engines: {node: '>=12'} @@ -3800,6 +4563,15 @@ packages: universalify: 2.0.0 dev: true + /fs-extra@11.1.1: + resolution: {integrity: sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ==} + engines: {node: '>=14.14'} + dependencies: + graceful-fs: 4.2.11 + jsonfile: 6.1.0 + universalify: 2.0.0 + dev: true + /fs-extra@7.0.1: resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==} engines: {node: '>=6 <7 || >=8'} @@ -3818,6 +4590,13 @@ packages: universalify: 0.1.2 dev: true + /fs-minipass@2.1.0: + resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==} + engines: {node: '>= 8'} + dependencies: + minipass: 3.3.6 + dev: true + /fs.realpath@1.0.0: resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} dev: true @@ -3847,6 +4626,10 @@ packages: resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} dev: true + /gcd@0.0.1: + resolution: {integrity: sha512-VNx3UEGr+ILJTiMs1+xc5SX1cMgJCrXezKPa003APUWNqQqaF6n25W8VcR7nHN6yRWbvvUTwCpZCFJeWC2kXlw==} + dev: true + /gensync@1.0.0-beta.2: resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} engines: {node: '>=6.9.0'} @@ -3916,6 +4699,10 @@ packages: resolution: {integrity: sha512-LF8VeHeR7v+wAbXqfgRlTSX/1BJR9Q1vEMR8JAz1cEg6GX07+zyj3sAdDvYjj/xnlIfVuGgj4qBei1K3hKH+PA==} dev: true + /github-from-package@0.0.0: + resolution: {integrity: sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==} + dev: true + /glob-parent@5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} engines: {node: '>= 6'} @@ -4018,6 +4805,40 @@ packages: get-intrinsic: 1.2.1 dev: true + /got@12.6.1: + resolution: {integrity: sha512-mThBblvlAF1d4O5oqyvN+ZxLAYwIJK7bpMxgYqPD9okW0C3qm5FFn7k811QrcuEBwaogR3ngOFoCfs6mRv7teQ==} + engines: {node: '>=14.16'} + dependencies: + '@sindresorhus/is': 5.6.0 + '@szmarczak/http-timer': 5.0.1 + cacheable-lookup: 7.0.0 + cacheable-request: 10.2.14 + decompress-response: 6.0.0 + form-data-encoder: 2.1.4 + get-stream: 6.0.1 + http2-wrapper: 2.2.1 + lowercase-keys: 3.0.0 + p-cancelable: 3.0.0 + responselike: 3.0.0 + dev: true + + /got@13.0.0: + resolution: {integrity: sha512-XfBk1CxOOScDcMr9O1yKkNaQyy865NbYs+F7dr4H0LZMVgCj2Le59k6PqbNHoL5ToeaEQUYh6c6yMfVcc6SJxA==} + engines: {node: '>=16'} + dependencies: + '@sindresorhus/is': 5.6.0 + '@szmarczak/http-timer': 5.0.1 + cacheable-lookup: 7.0.0 + cacheable-request: 10.2.14 + decompress-response: 6.0.0 + form-data-encoder: 2.1.4 + get-stream: 6.0.1 + http2-wrapper: 2.2.1 + lowercase-keys: 3.0.0 + p-cancelable: 3.0.0 + responselike: 3.0.0 + dev: true + /graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} dev: true @@ -4043,6 +4864,16 @@ packages: engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0} dev: true + /gray-matter@4.0.3: + resolution: {integrity: sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==} + engines: {node: '>=6.0'} + dependencies: + js-yaml: 3.14.1 + kind-of: 6.0.3 + section-matter: 1.0.0 + strip-bom-string: 1.0.0 + dev: true + /handlebars@4.7.8: resolution: {integrity: sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==} engines: {node: '>=0.4.7'} @@ -4124,6 +4955,10 @@ packages: resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} dev: true + /http-cache-semantics@4.1.1: + resolution: {integrity: sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==} + dev: true + /http-errors@2.0.0: resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} engines: {node: '>= 0.8'} @@ -4145,6 +4980,14 @@ packages: - supports-color dev: true + /http2-wrapper@2.2.1: + resolution: {integrity: sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ==} + engines: {node: '>=10.19.0'} + dependencies: + quick-lru: 5.1.1 + resolve-alpn: 1.2.1 + dev: true + /https-proxy-agent@7.0.2: resolution: {integrity: sha512-NmLNjm6ucYwtcUmL7JQC1ZQ57LmHP4lT15FQ8D61nak1rO6DH+fz5qNK2Ap5UN4ZapYICE3/0KodcLYSPsPbaA==} engines: {node: '>= 14'} @@ -4203,6 +5046,11 @@ packages: engines: {node: '>=8'} dev: true + /indent-string@5.0.0: + resolution: {integrity: sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==} + engines: {node: '>=12'} + dev: true + /inflight@1.0.6: resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} dependencies: @@ -4258,6 +5106,27 @@ packages: wrap-ansi: 6.2.0 dev: true + /inquirer@9.2.12: + resolution: {integrity: sha512-mg3Fh9g2zfuVWJn6lhST0O7x4n03k7G8Tx5nvikJkbq8/CK47WDVm+UznF0G6s5Zi0KcyUisr6DU8T67N5U+1Q==} + engines: {node: '>=14.18.0'} + dependencies: + '@ljharb/through': 2.3.11 + ansi-escapes: 4.3.2 + chalk: 5.3.0 + cli-cursor: 3.1.0 + cli-width: 4.1.0 + external-editor: 3.1.0 + figures: 5.0.0 + lodash: 4.17.21 + mute-stream: 1.0.0 + ora: 5.4.1 + run-async: 3.0.0 + rxjs: 7.8.1 + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 6.2.0 + dev: true + /internal-slot@1.0.5: resolution: {integrity: sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==} engines: {node: '>= 0.4'} @@ -4267,6 +5136,11 @@ packages: side-channel: 1.0.4 dev: true + /ip-regex@4.3.0: + resolution: {integrity: sha512-B9ZWJxHHOHUhUjCPrMpLD4xEq35bUTClHM1S6CBU5ixQnkZmwipwgc96vAd7AAGM9TGHvJR+Uss+/Ak6UphK+Q==} + engines: {node: '>=8'} + dev: true + /ip@1.1.8: resolution: {integrity: sha512-PuExPYUiu6qMBQb4l06ecm6T6ujzhmh+MeJcW9wa89PoAz5pvd4zPgN5WJV104mb6S2T1AwNIAaB70JNrLQWhg==} dev: true @@ -4280,6 +5154,22 @@ packages: engines: {node: '>= 0.10'} dev: true + /is-absolute-url@4.0.1: + resolution: {integrity: sha512-/51/TKE88Lmm7Gc4/8btclNXWS+g50wXhYJq8HWIBAGUBnoAdRu1aXeh364t/O7wXDAcTJDP8PNuNKWUDWie+A==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dev: true + + /is-alphabetical@2.0.1: + resolution: {integrity: sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==} + dev: true + + /is-alphanumerical@2.0.1: + resolution: {integrity: sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==} + dependencies: + is-alphabetical: 2.0.1 + is-decimal: 2.0.1 + dev: true + /is-arguments@1.1.1: resolution: {integrity: sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==} engines: {node: '>= 0.4'} @@ -4300,6 +5190,10 @@ packages: resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} dev: true + /is-arrayish@0.3.2: + resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==} + dev: true + /is-async-function@2.0.0: resolution: {integrity: sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==} engines: {node: '>= 0.4'} @@ -4328,6 +5222,11 @@ packages: has-tostringtag: 1.0.0 dev: true + /is-buffer@2.0.5: + resolution: {integrity: sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==} + engines: {node: '>=4'} + dev: true + /is-builtin-module@3.2.1: resolution: {integrity: sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==} engines: {node: '>=6'} @@ -4360,6 +5259,10 @@ packages: has-tostringtag: 1.0.0 dev: true + /is-decimal@2.0.1: + resolution: {integrity: sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==} + dev: true + /is-docker@2.2.1: resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==} engines: {node: '>=8'} @@ -4372,6 +5275,11 @@ packages: hasBin: true dev: true + /is-extendable@0.1.1: + resolution: {integrity: sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==} + engines: {node: '>=0.10.0'} + dev: true + /is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} @@ -4402,6 +5310,10 @@ packages: is-extglob: 2.1.1 dev: true + /is-hexadecimal@2.0.1: + resolution: {integrity: sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==} + dev: true + /is-inside-container@1.0.0: resolution: {integrity: sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==} engines: {node: '>=14.16'} @@ -4415,6 +5327,18 @@ packages: engines: {node: '>=8'} dev: true + /is-interactive@2.0.0: + resolution: {integrity: sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==} + engines: {node: '>=12'} + dev: true + + /is-ip@3.1.0: + resolution: {integrity: sha512-35vd5necO7IitFPjd/YBeqwWnyDWbuLH9ZXQdMfDA8TEo7pv5X8yfrvVO3xbJbLUlERCMvf6X0hTUamQxCYJ9Q==} + engines: {node: '>=8'} + dependencies: + ip-regex: 4.3.0 + dev: true + /is-lower-case@1.1.3: resolution: {integrity: sha512-+5A1e/WJpLLXZEDlgz4G//WYSHyQBD32qa4Jd3Lw06qQlv3fJHnp3YIHjTQSGzHMgzmVKz2ZP3rBxTHkPw/lxA==} dependencies: @@ -4446,6 +5370,16 @@ packages: engines: {node: '>=0.12.0'} dev: true + /is-online@10.0.0: + resolution: {integrity: sha512-WCPdKwNDjXJJmUubf2VHLMDBkUZEtuOvpXUfUnUFbEnM6In9ByiScL4f4jKACz/fsb2qDkesFerW3snf/AYz3A==} + engines: {node: '>=14.16'} + dependencies: + got: 12.6.1 + p-any: 4.0.0 + p-timeout: 5.1.0 + public-ip: 5.0.0 + dev: true + /is-path-cwd@2.2.0: resolution: {integrity: sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==} engines: {node: '>=6'} @@ -4466,6 +5400,11 @@ packages: engines: {node: '>=12'} dev: true + /is-plain-object@5.0.0: + resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==} + engines: {node: '>=0.10.0'} + dev: true + /is-regex@1.1.4: resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} engines: {node: '>= 0.4'} @@ -4527,6 +5466,11 @@ packages: engines: {node: '>=10'} dev: true + /is-unicode-supported@1.3.0: + resolution: {integrity: sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==} + engines: {node: '>=12'} + dev: true + /is-upper-case@1.1.2: resolution: {integrity: sha512-GQYSJMgfeAmVwh9ixyk888l7OIhNAGKtY6QA+IrWlu9MDTCaXmeozOZ2S9Knj7bQwBO/H6J2kb+pbyTUiMNbsw==} dependencies: @@ -4680,6 +5624,10 @@ packages: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} dev: true + /json-schema-traverse@1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + dev: true + /json-stable-stringify-without-jsonify@1.0.1: resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} dev: true @@ -4724,6 +5672,13 @@ packages: object.values: 1.1.7 dev: true + /katex@0.16.9: + resolution: {integrity: sha512-fsSYjWS0EEOwvy81j3vRA8TEAhQhKiqO+FQaKWp0m39qwOzHVBgAUBIXWj1pB+O2W3fIpNa6Y9KSKCVbfPhyAQ==} + hasBin: true + dependencies: + commander: 8.3.0 + dev: true + /keyv@4.5.3: resolution: {integrity: sha512-QCiSav9WaX1PgETJ+SpNnx2PRRapJ/oRSXM4VO5OGYGSjrxbKPVFVhB3l2OCbLCk329N8qyAtsJjSjvVBWzEug==} dependencies: @@ -4750,6 +5705,12 @@ packages: language-subtag-registry: 0.3.22 dev: true + /lcm@0.0.3: + resolution: {integrity: sha512-TB+ZjoillV6B26Vspf9l2L/vKaRY/4ep3hahcyVkCGFgsTNRUQdc24bQeNFiZeoxH0vr5+7SfNRMQuPHv/1IrQ==} + dependencies: + gcd: 0.0.1 + dev: true + /levn@0.4.1: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} @@ -4835,6 +5796,18 @@ packages: is-unicode-supported: 0.1.0 dev: true + /log-symbols@5.1.0: + resolution: {integrity: sha512-l0x2DvrW294C9uDCoQe1VSU4gf529FkSZ6leBl4TiqZH/e+0R7hSfHQBNut2mNygDgHwvYHfFLn6Oxb3VWj2rA==} + engines: {node: '>=12'} + dependencies: + chalk: 5.3.0 + is-unicode-supported: 1.3.0 + dev: true + + /longest-streak@3.1.0: + resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} + dev: true + /loose-envify@1.4.0: resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} hasBin: true @@ -4857,6 +5830,11 @@ packages: resolution: {integrity: sha512-2Fgx1Ycm599x+WGpIYwJOvsjmXFzTSc34IwDWALRA/8AopUKAVPwfJ+h5+f85BCp0PWmmJcWzEpxOpoXycMpdA==} dev: true + /lowercase-keys@3.0.0: + resolution: {integrity: sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dev: true + /lru-cache@4.1.5: resolution: {integrity: sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==} dependencies: @@ -4915,26 +5893,211 @@ packages: engines: {node: '>=8'} dev: true - /media-typer@0.3.0: - resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} - engines: {node: '>= 0.6'} + /markdown-table@3.0.3: + resolution: {integrity: sha512-Z1NL3Tb1M9wH4XESsCDEksWoKTdlUafKc4pt0GRwjUyXaCFZ+dc3g2erqB6zm3szA2IUSi7VnPI+o/9jnxh9hw==} dev: true - /meow@6.1.1: - resolution: {integrity: sha512-3YffViIt2QWgTy6Pale5QpopX/IvU3LPL03jOTqp6pGj3VjesdO/U8CuHMKpnQr4shCNCM5fd5XFFvIIl6JBHg==} - engines: {node: '>=8'} + /mdast-util-find-and-replace@2.2.2: + resolution: {integrity: sha512-MTtdFRz/eMDHXzeK6W3dO7mXUlF82Gom4y0oOgvHhh/HXZAGvIQDUvQ0SuUx+j2tv44b8xTHOm8K/9OoRFnXKw==} dependencies: - '@types/minimist': 1.2.3 - camelcase-keys: 6.2.2 - decamelize-keys: 1.1.1 - hard-rejection: 2.1.0 - minimist-options: 4.1.0 - normalize-package-data: 2.5.0 - read-pkg-up: 7.0.1 - redent: 3.0.0 - trim-newlines: 3.0.1 - type-fest: 0.13.1 - yargs-parser: 18.1.3 + '@types/mdast': 3.0.15 + escape-string-regexp: 5.0.0 + unist-util-is: 5.2.1 + unist-util-visit-parents: 5.1.3 + dev: true + + /mdast-util-from-markdown@1.3.1: + resolution: {integrity: sha512-4xTO/M8c82qBcnQc1tgpNtubGUW/Y1tBQ1B0i5CtSoelOLKFYlElIr3bvgREYYO5iRqbMY1YuqZng0GVOI8Qww==} + dependencies: + '@types/mdast': 3.0.15 + '@types/unist': 2.0.10 + decode-named-character-reference: 1.0.2 + mdast-util-to-string: 3.2.0 + micromark: 3.2.0 + micromark-util-decode-numeric-character-reference: 1.1.0 + micromark-util-decode-string: 1.1.0 + micromark-util-normalize-identifier: 1.1.0 + micromark-util-symbol: 1.1.0 + micromark-util-types: 1.1.0 + unist-util-stringify-position: 3.0.3 + uvu: 0.5.6 + transitivePeerDependencies: + - supports-color + dev: true + + /mdast-util-frontmatter@1.0.1: + resolution: {integrity: sha512-JjA2OjxRqAa8wEG8hloD0uTU0kdn8kbtOWpPP94NBkfAlbxn4S8gCGf/9DwFtEeGPXrDcNXdiDjVaRdUFqYokw==} + dependencies: + '@types/mdast': 3.0.15 + mdast-util-to-markdown: 1.5.0 + micromark-extension-frontmatter: 1.1.1 + dev: true + + /mdast-util-gfm-autolink-literal@1.0.3: + resolution: {integrity: sha512-My8KJ57FYEy2W2LyNom4n3E7hKTuQk/0SES0u16tjA9Z3oFkF4RrC/hPAPgjlSpezsOvI8ObcXcElo92wn5IGA==} + dependencies: + '@types/mdast': 3.0.15 + ccount: 2.0.1 + mdast-util-find-and-replace: 2.2.2 + micromark-util-character: 1.2.0 + dev: true + + /mdast-util-gfm-footnote@1.0.2: + resolution: {integrity: sha512-56D19KOGbE00uKVj3sgIykpwKL179QsVFwx/DCW0u/0+URsryacI4MAdNJl0dh+u2PSsD9FtxPFbHCzJ78qJFQ==} + dependencies: + '@types/mdast': 3.0.15 + mdast-util-to-markdown: 1.5.0 + micromark-util-normalize-identifier: 1.1.0 + dev: true + + /mdast-util-gfm-strikethrough@1.0.3: + resolution: {integrity: sha512-DAPhYzTYrRcXdMjUtUjKvW9z/FNAMTdU0ORyMcbmkwYNbKocDpdk+PX1L1dQgOID/+vVs1uBQ7ElrBQfZ0cuiQ==} + dependencies: + '@types/mdast': 3.0.15 + mdast-util-to-markdown: 1.5.0 + dev: true + + /mdast-util-gfm-table@1.0.7: + resolution: {integrity: sha512-jjcpmNnQvrmN5Vx7y7lEc2iIOEytYv7rTvu+MeyAsSHTASGCCRA79Igg2uKssgOs1i1po8s3plW0sTu1wkkLGg==} + dependencies: + '@types/mdast': 3.0.15 + markdown-table: 3.0.3 + mdast-util-from-markdown: 1.3.1 + mdast-util-to-markdown: 1.5.0 + transitivePeerDependencies: + - supports-color + dev: true + + /mdast-util-gfm-task-list-item@1.0.2: + resolution: {integrity: sha512-PFTA1gzfp1B1UaiJVyhJZA1rm0+Tzn690frc/L8vNX1Jop4STZgOE6bxUhnzdVSB+vm2GU1tIsuQcA9bxTQpMQ==} + dependencies: + '@types/mdast': 3.0.15 + mdast-util-to-markdown: 1.5.0 + dev: true + + /mdast-util-gfm@2.0.2: + resolution: {integrity: sha512-qvZ608nBppZ4icQlhQQIAdc6S3Ffj9RGmzwUKUWuEICFnd1LVkN3EktF7ZHAgfcEdvZB5owU9tQgt99e2TlLjg==} + dependencies: + mdast-util-from-markdown: 1.3.1 + mdast-util-gfm-autolink-literal: 1.0.3 + mdast-util-gfm-footnote: 1.0.2 + mdast-util-gfm-strikethrough: 1.0.3 + mdast-util-gfm-table: 1.0.7 + mdast-util-gfm-task-list-item: 1.0.2 + mdast-util-to-markdown: 1.5.0 + transitivePeerDependencies: + - supports-color + dev: true + + /mdast-util-math@2.0.2: + resolution: {integrity: sha512-8gmkKVp9v6+Tgjtq6SYx9kGPpTf6FVYRa53/DLh479aldR9AyP48qeVOgNZ5X7QUK7nOy4yw7vg6mbiGcs9jWQ==} + dependencies: + '@types/mdast': 3.0.15 + longest-streak: 3.1.0 + mdast-util-to-markdown: 1.5.0 + dev: true + + /mdast-util-mdx-expression@1.3.2: + resolution: {integrity: sha512-xIPmR5ReJDu/DHH1OoIT1HkuybIfRGYRywC+gJtI7qHjCJp/M9jrmBEJW22O8lskDWm562BX2W8TiAwRTb0rKA==} + dependencies: + '@types/estree-jsx': 1.0.3 + '@types/hast': 2.3.8 + '@types/mdast': 3.0.15 + mdast-util-from-markdown: 1.3.1 + mdast-util-to-markdown: 1.5.0 + transitivePeerDependencies: + - supports-color + dev: true + + /mdast-util-mdx-jsx@2.1.4: + resolution: {integrity: sha512-DtMn9CmVhVzZx3f+optVDF8yFgQVt7FghCRNdlIaS3X5Bnym3hZwPbg/XW86vdpKjlc1PVj26SpnLGeJBXD3JA==} + dependencies: + '@types/estree-jsx': 1.0.3 + '@types/hast': 2.3.8 + '@types/mdast': 3.0.15 + '@types/unist': 2.0.10 + ccount: 2.0.1 + mdast-util-from-markdown: 1.3.1 + mdast-util-to-markdown: 1.5.0 + parse-entities: 4.0.1 + stringify-entities: 4.0.3 + unist-util-remove-position: 4.0.2 + unist-util-stringify-position: 3.0.3 + vfile-message: 3.1.4 + transitivePeerDependencies: + - supports-color + dev: true + + /mdast-util-mdx@2.0.1: + resolution: {integrity: sha512-38w5y+r8nyKlGvNjSEqWrhG0w5PmnRA+wnBvm+ulYCct7nsGYhFVb0lljS9bQav4psDAS1eGkP2LMVcZBi/aqw==} + dependencies: + mdast-util-from-markdown: 1.3.1 + mdast-util-mdx-expression: 1.3.2 + mdast-util-mdx-jsx: 2.1.4 + mdast-util-mdxjs-esm: 1.3.1 + mdast-util-to-markdown: 1.5.0 + transitivePeerDependencies: + - supports-color + dev: true + + /mdast-util-mdxjs-esm@1.3.1: + resolution: {integrity: sha512-SXqglS0HrEvSdUEfoXFtcg7DRl7S2cwOXc7jkuusG472Mmjag34DUDeOJUZtl+BVnyeO1frIgVpHlNRWc2gk/w==} + dependencies: + '@types/estree-jsx': 1.0.3 + '@types/hast': 2.3.8 + '@types/mdast': 3.0.15 + mdast-util-from-markdown: 1.3.1 + mdast-util-to-markdown: 1.5.0 + transitivePeerDependencies: + - supports-color + dev: true + + /mdast-util-phrasing@3.0.1: + resolution: {integrity: sha512-WmI1gTXUBJo4/ZmSk79Wcb2HcjPJBzM1nlI/OUWA8yk2X9ik3ffNbBGsU+09BFmXaL1IBb9fiuvq6/KMiNycSg==} + dependencies: + '@types/mdast': 3.0.15 + unist-util-is: 5.2.1 + dev: true + + /mdast-util-to-markdown@1.5.0: + resolution: {integrity: sha512-bbv7TPv/WC49thZPg3jXuqzuvI45IL2EVAr/KxF0BSdHsU0ceFHOmwQn6evxAh1GaoK/6GQ1wp4R4oW2+LFL/A==} + dependencies: + '@types/mdast': 3.0.15 + '@types/unist': 2.0.10 + longest-streak: 3.1.0 + mdast-util-phrasing: 3.0.1 + mdast-util-to-string: 3.2.0 + micromark-util-decode-string: 1.1.0 + unist-util-visit: 4.1.2 + zwitch: 2.0.4 + dev: true + + /mdast-util-to-string@3.2.0: + resolution: {integrity: sha512-V4Zn/ncyN1QNSqSBxTrMOLpjr+IKdHl2v3KVLoWmDPscP4r9GcCi71gjgvUV1SFSKh92AjAG4peFuBl2/YgCJg==} + dependencies: + '@types/mdast': 3.0.15 + dev: true + + /media-typer@0.3.0: + resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} + engines: {node: '>= 0.6'} + dev: true + + /meow@6.1.1: + resolution: {integrity: sha512-3YffViIt2QWgTy6Pale5QpopX/IvU3LPL03jOTqp6pGj3VjesdO/U8CuHMKpnQr4shCNCM5fd5XFFvIIl6JBHg==} + engines: {node: '>=8'} + dependencies: + '@types/minimist': 1.2.3 + camelcase-keys: 6.2.2 + decamelize-keys: 1.1.1 + hard-rejection: 2.1.0 + minimist-options: 4.1.0 + normalize-package-data: 2.5.0 + read-pkg-up: 7.0.1 + redent: 3.0.0 + trim-newlines: 3.0.1 + type-fest: 0.13.1 + yargs-parser: 18.1.3 dev: true /merge-descriptors@1.0.1: @@ -4955,6 +6118,361 @@ packages: engines: {node: '>= 0.6'} dev: true + /micromark-core-commonmark@1.1.0: + resolution: {integrity: sha512-BgHO1aRbolh2hcrzL2d1La37V0Aoz73ymF8rAcKnohLy93titmv62E0gP8Hrx9PKcKrqCZ1BbLGbP3bEhoXYlw==} + dependencies: + decode-named-character-reference: 1.0.2 + micromark-factory-destination: 1.1.0 + micromark-factory-label: 1.1.0 + micromark-factory-space: 1.1.0 + micromark-factory-title: 1.1.0 + micromark-factory-whitespace: 1.1.0 + micromark-util-character: 1.2.0 + micromark-util-chunked: 1.1.0 + micromark-util-classify-character: 1.1.0 + micromark-util-html-tag-name: 1.2.0 + micromark-util-normalize-identifier: 1.1.0 + micromark-util-resolve-all: 1.1.0 + micromark-util-subtokenize: 1.1.0 + micromark-util-symbol: 1.1.0 + micromark-util-types: 1.1.0 + uvu: 0.5.6 + dev: true + + /micromark-extension-frontmatter@1.1.1: + resolution: {integrity: sha512-m2UH9a7n3W8VAH9JO9y01APpPKmNNNs71P0RbknEmYSaZU5Ghogv38BYO94AI5Xw6OYfxZRdHZZ2nYjs/Z+SZQ==} + dependencies: + fault: 2.0.1 + micromark-util-character: 1.2.0 + micromark-util-symbol: 1.1.0 + micromark-util-types: 1.1.0 + dev: true + + /micromark-extension-gfm-autolink-literal@1.0.5: + resolution: {integrity: sha512-z3wJSLrDf8kRDOh2qBtoTRD53vJ+CWIyo7uyZuxf/JAbNJjiHsOpG1y5wxk8drtv3ETAHutCu6N3thkOOgueWg==} + dependencies: + micromark-util-character: 1.2.0 + micromark-util-sanitize-uri: 1.2.0 + micromark-util-symbol: 1.1.0 + micromark-util-types: 1.1.0 + dev: true + + /micromark-extension-gfm-footnote@1.1.2: + resolution: {integrity: sha512-Yxn7z7SxgyGWRNa4wzf8AhYYWNrwl5q1Z8ii+CSTTIqVkmGZF1CElX2JI8g5yGoM3GAman9/PVCUFUSJ0kB/8Q==} + dependencies: + micromark-core-commonmark: 1.1.0 + micromark-factory-space: 1.1.0 + micromark-util-character: 1.2.0 + micromark-util-normalize-identifier: 1.1.0 + micromark-util-sanitize-uri: 1.2.0 + micromark-util-symbol: 1.1.0 + micromark-util-types: 1.1.0 + uvu: 0.5.6 + dev: true + + /micromark-extension-gfm-strikethrough@1.0.7: + resolution: {integrity: sha512-sX0FawVE1o3abGk3vRjOH50L5TTLr3b5XMqnP9YDRb34M0v5OoZhG+OHFz1OffZ9dlwgpTBKaT4XW/AsUVnSDw==} + dependencies: + micromark-util-chunked: 1.1.0 + micromark-util-classify-character: 1.1.0 + micromark-util-resolve-all: 1.1.0 + micromark-util-symbol: 1.1.0 + micromark-util-types: 1.1.0 + uvu: 0.5.6 + dev: true + + /micromark-extension-gfm-table@1.0.7: + resolution: {integrity: sha512-3ZORTHtcSnMQEKtAOsBQ9/oHp9096pI/UvdPtN7ehKvrmZZ2+bbWhi0ln+I9drmwXMt5boocn6OlwQzNXeVeqw==} + dependencies: + micromark-factory-space: 1.1.0 + micromark-util-character: 1.2.0 + micromark-util-symbol: 1.1.0 + micromark-util-types: 1.1.0 + uvu: 0.5.6 + dev: true + + /micromark-extension-gfm-tagfilter@1.0.2: + resolution: {integrity: sha512-5XWB9GbAUSHTn8VPU8/1DBXMuKYT5uOgEjJb8gN3mW0PNW5OPHpSdojoqf+iq1xo7vWzw/P8bAHY0n6ijpXF7g==} + dependencies: + micromark-util-types: 1.1.0 + dev: true + + /micromark-extension-gfm-task-list-item@1.0.5: + resolution: {integrity: sha512-RMFXl2uQ0pNQy6Lun2YBYT9g9INXtWJULgbt01D/x8/6yJ2qpKyzdZD3pi6UIkzF++Da49xAelVKUeUMqd5eIQ==} + dependencies: + micromark-factory-space: 1.1.0 + micromark-util-character: 1.2.0 + micromark-util-symbol: 1.1.0 + micromark-util-types: 1.1.0 + uvu: 0.5.6 + dev: true + + /micromark-extension-gfm@2.0.3: + resolution: {integrity: sha512-vb9OoHqrhCmbRidQv/2+Bc6pkP0FrtlhurxZofvOEy5o8RtuuvTq+RQ1Vw5ZDNrVraQZu3HixESqbG+0iKk/MQ==} + dependencies: + micromark-extension-gfm-autolink-literal: 1.0.5 + micromark-extension-gfm-footnote: 1.1.2 + micromark-extension-gfm-strikethrough: 1.0.7 + micromark-extension-gfm-table: 1.0.7 + micromark-extension-gfm-tagfilter: 1.0.2 + micromark-extension-gfm-task-list-item: 1.0.5 + micromark-util-combine-extensions: 1.1.0 + micromark-util-types: 1.1.0 + dev: true + + /micromark-extension-math@2.1.2: + resolution: {integrity: sha512-es0CcOV89VNS9wFmyn+wyFTKweXGW4CEvdaAca6SWRWPyYCbBisnjaHLjWO4Nszuiud84jCpkHsqAJoa768Pvg==} + dependencies: + '@types/katex': 0.16.6 + katex: 0.16.9 + micromark-factory-space: 1.1.0 + micromark-util-character: 1.2.0 + micromark-util-symbol: 1.1.0 + micromark-util-types: 1.1.0 + uvu: 0.5.6 + dev: true + + /micromark-extension-mdx-expression@1.0.8: + resolution: {integrity: sha512-zZpeQtc5wfWKdzDsHRBY003H2Smg+PUi2REhqgIhdzAa5xonhP03FcXxqFSerFiNUr5AWmHpaNPQTBVOS4lrXw==} + dependencies: + '@types/estree': 1.0.5 + micromark-factory-mdx-expression: 1.0.9 + micromark-factory-space: 1.1.0 + micromark-util-character: 1.2.0 + micromark-util-events-to-acorn: 1.2.3 + micromark-util-symbol: 1.1.0 + micromark-util-types: 1.1.0 + uvu: 0.5.6 + dev: true + + /micromark-extension-mdx-jsx@1.0.5: + resolution: {integrity: sha512-gPH+9ZdmDflbu19Xkb8+gheqEDqkSpdCEubQyxuz/Hn8DOXiXvrXeikOoBA71+e8Pfi0/UYmU3wW3H58kr7akA==} + dependencies: + '@types/acorn': 4.0.6 + '@types/estree': 1.0.5 + estree-util-is-identifier-name: 2.1.0 + micromark-factory-mdx-expression: 1.0.9 + micromark-factory-space: 1.1.0 + micromark-util-character: 1.2.0 + micromark-util-symbol: 1.1.0 + micromark-util-types: 1.1.0 + uvu: 0.5.6 + vfile-message: 3.1.4 + dev: true + + /micromark-extension-mdx-md@1.0.1: + resolution: {integrity: sha512-7MSuj2S7xjOQXAjjkbjBsHkMtb+mDGVW6uI2dBL9snOBCbZmoNgDAeZ0nSn9j3T42UE/g2xVNMn18PJxZvkBEA==} + dependencies: + micromark-util-types: 1.1.0 + dev: true + + /micromark-extension-mdxjs-esm@1.0.5: + resolution: {integrity: sha512-xNRBw4aoURcyz/S69B19WnZAkWJMxHMT5hE36GtDAyhoyn/8TuAeqjFJQlwk+MKQsUD7b3l7kFX+vlfVWgcX1w==} + dependencies: + '@types/estree': 1.0.5 + micromark-core-commonmark: 1.1.0 + micromark-util-character: 1.2.0 + micromark-util-events-to-acorn: 1.2.3 + micromark-util-symbol: 1.1.0 + micromark-util-types: 1.1.0 + unist-util-position-from-estree: 1.1.2 + uvu: 0.5.6 + vfile-message: 3.1.4 + dev: true + + /micromark-extension-mdxjs@1.0.1: + resolution: {integrity: sha512-7YA7hF6i5eKOfFUzZ+0z6avRG52GpWR8DL+kN47y3f2KhxbBZMhmxe7auOeaTBrW2DenbbZTf1ea9tA2hDpC2Q==} + dependencies: + acorn: 8.10.0 + acorn-jsx: 5.3.2(acorn@8.10.0) + micromark-extension-mdx-expression: 1.0.8 + micromark-extension-mdx-jsx: 1.0.5 + micromark-extension-mdx-md: 1.0.1 + micromark-extension-mdxjs-esm: 1.0.5 + micromark-util-combine-extensions: 1.1.0 + micromark-util-types: 1.1.0 + dev: true + + /micromark-factory-destination@1.1.0: + resolution: {integrity: sha512-XaNDROBgx9SgSChd69pjiGKbV+nfHGDPVYFs5dOoDd7ZnMAE+Cuu91BCpsY8RT2NP9vo/B8pds2VQNCLiu0zhg==} + dependencies: + micromark-util-character: 1.2.0 + micromark-util-symbol: 1.1.0 + micromark-util-types: 1.1.0 + dev: true + + /micromark-factory-label@1.1.0: + resolution: {integrity: sha512-OLtyez4vZo/1NjxGhcpDSbHQ+m0IIGnT8BoPamh+7jVlzLJBH98zzuCoUeMxvM6WsNeh8wx8cKvqLiPHEACn0w==} + dependencies: + micromark-util-character: 1.2.0 + micromark-util-symbol: 1.1.0 + micromark-util-types: 1.1.0 + uvu: 0.5.6 + dev: true + + /micromark-factory-mdx-expression@1.0.9: + resolution: {integrity: sha512-jGIWzSmNfdnkJq05c7b0+Wv0Kfz3NJ3N4cBjnbO4zjXIlxJr+f8lk+5ZmwFvqdAbUy2q6B5rCY//g0QAAaXDWA==} + dependencies: + '@types/estree': 1.0.5 + micromark-util-character: 1.2.0 + micromark-util-events-to-acorn: 1.2.3 + micromark-util-symbol: 1.1.0 + micromark-util-types: 1.1.0 + unist-util-position-from-estree: 1.1.2 + uvu: 0.5.6 + vfile-message: 3.1.4 + dev: true + + /micromark-factory-space@1.1.0: + resolution: {integrity: sha512-cRzEj7c0OL4Mw2v6nwzttyOZe8XY/Z8G0rzmWQZTBi/jjwyw/U4uqKtUORXQrR5bAZZnbTI/feRV/R7hc4jQYQ==} + dependencies: + micromark-util-character: 1.2.0 + micromark-util-types: 1.1.0 + dev: true + + /micromark-factory-title@1.1.0: + resolution: {integrity: sha512-J7n9R3vMmgjDOCY8NPw55jiyaQnH5kBdV2/UXCtZIpnHH3P6nHUKaH7XXEYuWwx/xUJcawa8plLBEjMPU24HzQ==} + dependencies: + micromark-factory-space: 1.1.0 + micromark-util-character: 1.2.0 + micromark-util-symbol: 1.1.0 + micromark-util-types: 1.1.0 + dev: true + + /micromark-factory-whitespace@1.1.0: + resolution: {integrity: sha512-v2WlmiymVSp5oMg+1Q0N1Lxmt6pMhIHD457whWM7/GUlEks1hI9xj5w3zbc4uuMKXGisksZk8DzP2UyGbGqNsQ==} + dependencies: + micromark-factory-space: 1.1.0 + micromark-util-character: 1.2.0 + micromark-util-symbol: 1.1.0 + micromark-util-types: 1.1.0 + dev: true + + /micromark-util-character@1.2.0: + resolution: {integrity: sha512-lXraTwcX3yH/vMDaFWCQJP1uIszLVebzUa3ZHdrgxr7KEU/9mL4mVgCpGbyhvNLNlauROiNUq7WN5u7ndbY6xg==} + dependencies: + micromark-util-symbol: 1.1.0 + micromark-util-types: 1.1.0 + dev: true + + /micromark-util-chunked@1.1.0: + resolution: {integrity: sha512-Ye01HXpkZPNcV6FiyoW2fGZDUw4Yc7vT0E9Sad83+bEDiCJ1uXu0S3mr8WLpsz3HaG3x2q0HM6CTuPdcZcluFQ==} + dependencies: + micromark-util-symbol: 1.1.0 + dev: true + + /micromark-util-classify-character@1.1.0: + resolution: {integrity: sha512-SL0wLxtKSnklKSUplok1WQFoGhUdWYKggKUiqhX+Swala+BtptGCu5iPRc+xvzJ4PXE/hwM3FNXsfEVgoZsWbw==} + dependencies: + micromark-util-character: 1.2.0 + micromark-util-symbol: 1.1.0 + micromark-util-types: 1.1.0 + dev: true + + /micromark-util-combine-extensions@1.1.0: + resolution: {integrity: sha512-Q20sp4mfNf9yEqDL50WwuWZHUrCO4fEyeDCnMGmG5Pr0Cz15Uo7KBs6jq+dq0EgX4DPwwrh9m0X+zPV1ypFvUA==} + dependencies: + micromark-util-chunked: 1.1.0 + micromark-util-types: 1.1.0 + dev: true + + /micromark-util-decode-numeric-character-reference@1.1.0: + resolution: {integrity: sha512-m9V0ExGv0jB1OT21mrWcuf4QhP46pH1KkfWy9ZEezqHKAxkj4mPCy3nIH1rkbdMlChLHX531eOrymlwyZIf2iw==} + dependencies: + micromark-util-symbol: 1.1.0 + dev: true + + /micromark-util-decode-string@1.1.0: + resolution: {integrity: sha512-YphLGCK8gM1tG1bd54azwyrQRjCFcmgj2S2GoJDNnh4vYtnL38JS8M4gpxzOPNyHdNEpheyWXCTnnTDY3N+NVQ==} + dependencies: + decode-named-character-reference: 1.0.2 + micromark-util-character: 1.2.0 + micromark-util-decode-numeric-character-reference: 1.1.0 + micromark-util-symbol: 1.1.0 + dev: true + + /micromark-util-encode@1.1.0: + resolution: {integrity: sha512-EuEzTWSTAj9PA5GOAs992GzNh2dGQO52UvAbtSOMvXTxv3Criqb6IOzJUBCmEqrrXSblJIJBbFFv6zPxpreiJw==} + dev: true + + /micromark-util-events-to-acorn@1.2.3: + resolution: {integrity: sha512-ij4X7Wuc4fED6UoLWkmo0xJQhsktfNh1J0m8g4PbIMPlx+ek/4YdW5mvbye8z/aZvAPUoxgXHrwVlXAPKMRp1w==} + dependencies: + '@types/acorn': 4.0.6 + '@types/estree': 1.0.5 + '@types/unist': 2.0.10 + estree-util-visit: 1.2.1 + micromark-util-symbol: 1.1.0 + micromark-util-types: 1.1.0 + uvu: 0.5.6 + vfile-message: 3.1.4 + dev: true + + /micromark-util-html-tag-name@1.2.0: + resolution: {integrity: sha512-VTQzcuQgFUD7yYztuQFKXT49KghjtETQ+Wv/zUjGSGBioZnkA4P1XXZPT1FHeJA6RwRXSF47yvJ1tsJdoxwO+Q==} + dev: true + + /micromark-util-normalize-identifier@1.1.0: + resolution: {integrity: sha512-N+w5vhqrBihhjdpM8+5Xsxy71QWqGn7HYNUvch71iV2PM7+E3uWGox1Qp90loa1ephtCxG2ftRV/Conitc6P2Q==} + dependencies: + micromark-util-symbol: 1.1.0 + dev: true + + /micromark-util-resolve-all@1.1.0: + resolution: {integrity: sha512-b/G6BTMSg+bX+xVCshPTPyAu2tmA0E4X98NSR7eIbeC6ycCqCeE7wjfDIgzEbkzdEVJXRtOG4FbEm/uGbCRouA==} + dependencies: + micromark-util-types: 1.1.0 + dev: true + + /micromark-util-sanitize-uri@1.2.0: + resolution: {integrity: sha512-QO4GXv0XZfWey4pYFndLUKEAktKkG5kZTdUNaTAkzbuJxn2tNBOr+QtxR2XpWaMhbImT2dPzyLrPXLlPhph34A==} + dependencies: + micromark-util-character: 1.2.0 + micromark-util-encode: 1.1.0 + micromark-util-symbol: 1.1.0 + dev: true + + /micromark-util-subtokenize@1.1.0: + resolution: {integrity: sha512-kUQHyzRoxvZO2PuLzMt2P/dwVsTiivCK8icYTeR+3WgbuPqfHgPPy7nFKbeqRivBvn/3N3GBiNC+JRTMSxEC7A==} + dependencies: + micromark-util-chunked: 1.1.0 + micromark-util-symbol: 1.1.0 + micromark-util-types: 1.1.0 + uvu: 0.5.6 + dev: true + + /micromark-util-symbol@1.1.0: + resolution: {integrity: sha512-uEjpEYY6KMs1g7QfJ2eX1SQEV+ZT4rUD3UcF6l57acZvLNK7PBZL+ty82Z1qhK1/yXIY4bdx04FKMgR0g4IAag==} + dev: true + + /micromark-util-types@1.1.0: + resolution: {integrity: sha512-ukRBgie8TIAcacscVHSiddHjO4k/q3pnedmzMQ4iwDcK0FtFCohKOlFbaOL/mPgfnPsL3C1ZyxJa4sbWrBl3jg==} + dev: true + + /micromark@3.2.0: + resolution: {integrity: sha512-uD66tJj54JLYq0De10AhWycZWGQNUvDI55xPgk2sQM5kn1JYlhbCMTtEeT27+vAhW2FBQxLlOmS3pmA7/2z4aA==} + dependencies: + '@types/debug': 4.1.9 + debug: 4.3.4 + decode-named-character-reference: 1.0.2 + micromark-core-commonmark: 1.1.0 + micromark-factory-space: 1.1.0 + micromark-util-character: 1.2.0 + micromark-util-chunked: 1.1.0 + micromark-util-combine-extensions: 1.1.0 + micromark-util-decode-numeric-character-reference: 1.1.0 + micromark-util-encode: 1.1.0 + micromark-util-normalize-identifier: 1.1.0 + micromark-util-resolve-all: 1.1.0 + micromark-util-sanitize-uri: 1.2.0 + micromark-util-subtokenize: 1.1.0 + micromark-util-symbol: 1.1.0 + micromark-util-types: 1.1.0 + uvu: 0.5.6 + transitivePeerDependencies: + - supports-color + dev: true + /micromatch@4.0.5: resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==} engines: {node: '>=8.6'} @@ -4997,6 +6515,16 @@ packages: engines: {node: '>=12'} dev: true + /mimic-response@3.1.0: + resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} + engines: {node: '>=10'} + dev: true + + /mimic-response@4.0.0: + resolution: {integrity: sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dev: true + /min-indent@1.0.1: resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} engines: {node: '>=4'} @@ -5050,11 +6578,49 @@ packages: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} dev: true + /minipass@3.3.6: + resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==} + engines: {node: '>=8'} + dependencies: + yallist: 4.0.0 + dev: true + + /minipass@5.0.0: + resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==} + engines: {node: '>=8'} + dev: true + + /minizlib@2.1.2: + resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==} + engines: {node: '>= 8'} + dependencies: + minipass: 3.3.6 + yallist: 4.0.0 + dev: true + + /mintlify@4.0.49: + resolution: {integrity: sha512-lEyVt/f1rVeGKRxDwF60kFx6kzUTrDjizzG4P2VdUYA89CZkHgqycykNTKboCiqhR6yhSWtWdrMQKcAMGamvRw==} + engines: {node: '>=18.0.0'} + hasBin: true + dependencies: + '@mintlify/cli': 4.0.49 + transitivePeerDependencies: + - bufferutil + - debug + - encoding + - supports-color + - utf-8-validate + dev: true + /mixme@0.5.9: resolution: {integrity: sha512-VC5fg6ySUscaWUpI4gxCBTQMH2RdUpNrk+MsbpCYtIvf9SBJdiUey4qE7BXviJsJR4nDQxCZ+3yaYNW3guz/Pw==} engines: {node: '>= 8.0.0'} dev: true + /mkdirp-classic@0.5.3: + resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==} + dev: true + /mkdirp@0.5.6: resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==} hasBin: true @@ -5062,6 +6628,12 @@ packages: minimist: 1.2.8 dev: true + /mkdirp@1.0.4: + resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} + engines: {node: '>=10'} + hasBin: true + dev: true + /mlly@1.4.2: resolution: {integrity: sha512-i/Ykufi2t1EZ6NaPLdfnZk2AX8cs0d+mTzVKuPfqPKPatxLApaBoxJQ9x1/uckXtrS/U5oisPMDkNs0yQTaBRg==} dependencies: @@ -5070,6 +6642,11 @@ packages: pkg-types: 1.0.3 ufo: 1.3.1 + /mri@1.2.0: + resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} + engines: {node: '>=4'} + dev: true + /ms@2.0.0: resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} dev: true @@ -5126,6 +6703,11 @@ packages: resolution: {integrity: sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==} dev: true + /mute-stream@1.0.0: + resolution: {integrity: sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + dev: true + /mz@2.7.0: resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} dependencies: @@ -5139,6 +6721,10 @@ packages: engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true + /napi-build-utils@1.0.2: + resolution: {integrity: sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==} + dev: true + /natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} dev: true @@ -5163,6 +6749,17 @@ packages: lower-case: 1.1.4 dev: true + /node-abi@3.51.0: + resolution: {integrity: sha512-SQkEP4hmNWjlniS5zdnfIXTk1x7Ome85RDzHlTbBtzE97Gfwz/Ipw4v/Ryk20DWIy3yCNVLVlGKApCnmvYoJbA==} + engines: {node: '>=10'} + dependencies: + semver: 7.5.4 + dev: true + + /node-addon-api@6.1.0: + resolution: {integrity: sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==} + dev: true + /node-fetch@2.7.0: resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} engines: {node: 4.x || >=6.0.0} @@ -5215,6 +6812,11 @@ packages: engines: {node: '>=0.10.0'} dev: true + /normalize-url@8.0.0: + resolution: {integrity: sha512-uVFpKhj5MheNBJRTiMZ9pE/7hD1QTeEvugSJW/OmLzAp78PB5O6adfMNTvmfKhXBkvCzC+rqifWcVYpGFwTjnw==} + engines: {node: '>=14.16'} + dev: true + /npm-run-path@4.0.1: resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} engines: {node: '>=8'} @@ -5323,6 +6925,15 @@ packages: mimic-fn: 4.0.0 dev: true + /open@8.4.2: + resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==} + engines: {node: '>=12'} + dependencies: + define-lazy-prop: 2.0.0 + is-docker: 2.2.1 + is-wsl: 2.2.0 + dev: true + /open@9.1.0: resolution: {integrity: sha512-OS+QTnw1/4vrf+9hh1jc1jnYjzSG4ttTBB8UxOwAnInG3Uo4ssetzC1ihqaIHjLJnA5GGlRl6QlZXOTQhRBUvg==} engines: {node: '>=14.16'} @@ -5333,6 +6944,10 @@ packages: is-wsl: 2.2.0 dev: true + /openapi-types@12.1.3: + resolution: {integrity: sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==} + dev: true + /optionator@0.9.3: resolution: {integrity: sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==} engines: {node: '>= 0.8.0'} @@ -5374,6 +6989,21 @@ packages: wcwidth: 1.0.1 dev: true + /ora@6.3.1: + resolution: {integrity: sha512-ERAyNnZOfqM+Ao3RAvIXkYh5joP220yf59gVe2X/cI6SiCxIdi4c9HZKZD8R6q/RDXEje1THBju6iExiSsgJaQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + chalk: 5.3.0 + cli-cursor: 4.0.0 + cli-spinners: 2.9.1 + is-interactive: 2.0.0 + is-unicode-supported: 1.3.0 + log-symbols: 5.1.0 + stdin-discarder: 0.1.0 + strip-ansi: 7.1.0 + wcwidth: 1.0.1 + dev: true + /os-tmpdir@1.0.2: resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==} engines: {node: '>=0.10.0'} @@ -5387,6 +7017,19 @@ packages: resolution: {integrity: sha512-AlWY719RF02ujitly7Kk/0QlV+pXGFDHrHf9O2OKqyqgBieaPOIeuSkL8sRK6j2WK+/ZAURq2kZsY0d8JapUiw==} dev: true + /p-any@4.0.0: + resolution: {integrity: sha512-S/B50s+pAVe0wmEZHmBs/9yJXeZ5KhHzOsgKzt0hRdgkoR3DxW9ts46fcsWi/r3VnzsnkKS7q4uimze+zjdryw==} + engines: {node: '>=12.20'} + dependencies: + p-cancelable: 3.0.0 + p-some: 6.0.0 + dev: true + + /p-cancelable@3.0.0: + resolution: {integrity: sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==} + engines: {node: '>=12.20'} + dev: true + /p-filter@2.1.0: resolution: {integrity: sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw==} engines: {node: '>=8'} @@ -5440,6 +7083,19 @@ packages: aggregate-error: 3.1.0 dev: true + /p-some@6.0.0: + resolution: {integrity: sha512-CJbQCKdfSX3fIh8/QKgS+9rjm7OBNUTmwWswAFQAhc8j1NR1dsEDETUEuVUtQHZpV+J03LqWBEwvu0g1Yn+TYg==} + engines: {node: '>=12.20'} + dependencies: + aggregate-error: 4.0.1 + p-cancelable: 3.0.0 + dev: true + + /p-timeout@5.1.0: + resolution: {integrity: sha512-auFDyzzzGZZZdHz3BtET9VEz0SE/uMEAx7uWfGPucfzEwwe/xH0iVeZibQmANYE/hp9T2+UUZT5m+BKyrDp3Ew==} + engines: {node: '>=12'} + dev: true + /p-try@2.2.0: resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} engines: {node: '>=6'} @@ -5483,6 +7139,19 @@ packages: callsites: 3.1.0 dev: true + /parse-entities@4.0.1: + resolution: {integrity: sha512-SWzvYcSJh4d/SGLIOQfZ/CoNv6BTlI6YEQ7Nj82oDVnRpwe/Z/F1EMx42x3JAOwGBlCjeCH0BRJQbQ/opHL17w==} + dependencies: + '@types/unist': 2.0.10 + character-entities: 2.0.2 + character-entities-legacy: 3.0.0 + character-reference-invalid: 2.0.1 + decode-named-character-reference: 1.0.2 + is-alphanumerical: 2.0.1 + is-decimal: 2.0.1 + is-hexadecimal: 2.0.1 + dev: true + /parse-json@5.2.0: resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} engines: {node: '>=8'} @@ -5615,6 +7284,25 @@ packages: picocolors: 1.0.0 source-map-js: 1.0.2 + /prebuild-install@7.1.1: + resolution: {integrity: sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==} + engines: {node: '>=10'} + hasBin: true + dependencies: + detect-libc: 2.0.2 + expand-template: 2.0.3 + github-from-package: 0.0.0 + minimist: 1.2.8 + mkdirp-classic: 0.5.3 + napi-build-utils: 1.0.2 + node-abi: 3.51.0 + pump: 3.0.0 + rc: 1.2.8 + simple-get: 4.0.1 + tar-fs: 2.1.1 + tunnel-agent: 0.6.0 + dev: true + /preferred-pm@3.1.2: resolution: {integrity: sha512-nk7dKrcW8hfCZ4H6klWcdRknBOXWzNQByJ0oJyX97BOupsYD+FzLS4hflgEu/uPUEHZCuRfMxzCBsuWd7OzT8Q==} engines: {node: '>=10'} @@ -5762,6 +7450,22 @@ packages: resolution: {integrity: sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==} dev: true + /public-ip@5.0.0: + resolution: {integrity: sha512-xaH3pZMni/R2BG7ZXXaWS9Wc9wFlhyDVJF47IJ+3ali0TGv+2PsckKxbmo+rnx3ZxiV2wblVhtdS3bohAP6GGw==} + engines: {node: ^14.13.1 || >=16.0.0} + dependencies: + dns-socket: 4.2.2 + got: 12.6.1 + is-ip: 3.1.0 + dev: true + + /pump@3.0.0: + resolution: {integrity: sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==} + dependencies: + end-of-stream: 1.4.4 + once: 1.4.0 + dev: true + /punycode@2.3.0: resolution: {integrity: sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==} engines: {node: '>=6'} @@ -5778,11 +7482,20 @@ packages: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} dev: true + /queue-tick@1.0.1: + resolution: {integrity: sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==} + dev: true + /quick-lru@4.0.1: resolution: {integrity: sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==} engines: {node: '>=8'} dev: true + /quick-lru@5.1.1: + resolution: {integrity: sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==} + engines: {node: '>=10'} + dev: true + /range-parser@1.2.1: resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} engines: {node: '>= 0.6'} @@ -5919,15 +7632,91 @@ packages: jsesc: 0.5.0 dev: true + /remark-frontmatter@4.0.1: + resolution: {integrity: sha512-38fJrB0KnmD3E33a5jZC/5+gGAC2WKNiPw1/fdXJvijBlhA7RCsvJklrYJakS0HedninvaCYW8lQGf9C918GfA==} + dependencies: + '@types/mdast': 3.0.15 + mdast-util-frontmatter: 1.0.1 + micromark-extension-frontmatter: 1.1.1 + unified: 10.1.2 + dev: true + + /remark-gfm@3.0.1: + resolution: {integrity: sha512-lEFDoi2PICJyNrACFOfDD3JlLkuSbOa5Wd8EPt06HUdptv8Gn0bxYTdbU/XXQ3swAPkEaGxxPN9cbnMHvVu1Ig==} + dependencies: + '@types/mdast': 3.0.15 + mdast-util-gfm: 2.0.2 + micromark-extension-gfm: 2.0.3 + unified: 10.1.2 + transitivePeerDependencies: + - supports-color + dev: true + + /remark-math@5.1.1: + resolution: {integrity: sha512-cE5T2R/xLVtfFI4cCePtiRn+e6jKMtFDR3P8V3qpv8wpKjwvHoBA4eJzvX+nVrnlNy0911bdGmuspCSwetfYHw==} + dependencies: + '@types/mdast': 3.0.15 + mdast-util-math: 2.0.2 + micromark-extension-math: 2.1.2 + unified: 10.1.2 + dev: true + + /remark-mdx@2.3.0: + resolution: {integrity: sha512-g53hMkpM0I98MU266IzDFMrTD980gNF3BJnkyFcmN+dD873mQeD5rdMO3Y2X+x8umQfbSE0PcoEDl7ledSA+2g==} + dependencies: + mdast-util-mdx: 2.0.1 + micromark-extension-mdxjs: 1.0.1 + transitivePeerDependencies: + - supports-color + dev: true + + /remark-parse@10.0.2: + resolution: {integrity: sha512-3ydxgHa/ZQzG8LvC7jTXccARYDcRld3VfcgIIFs7bI6vbRSxJJmzgLEIIoYKyrfhaY+ujuWaf/PJiMZXoiCXgw==} + dependencies: + '@types/mdast': 3.0.15 + mdast-util-from-markdown: 1.3.1 + unified: 10.1.2 + transitivePeerDependencies: + - supports-color + dev: true + + /remark-stringify@10.0.3: + resolution: {integrity: sha512-koyOzCMYoUHudypbj4XpnAKFbkddRMYZHwghnxd7ue5210WzGw6kOBwauJTRUMq16jsovXx8dYNvSSWP89kZ3A==} + dependencies: + '@types/mdast': 3.0.15 + mdast-util-to-markdown: 1.5.0 + unified: 10.1.2 + dev: true + + /remark@14.0.3: + resolution: {integrity: sha512-bfmJW1dmR2LvaMJuAnE88pZP9DktIFYXazkTfOIKZzi3Knk9lT0roItIA24ydOucI3bV/g/tXBA6hzqq3FV9Ew==} + dependencies: + '@types/mdast': 3.0.15 + remark-parse: 10.0.2 + remark-stringify: 10.0.3 + unified: 10.1.2 + transitivePeerDependencies: + - supports-color + dev: true + /require-directory@2.1.1: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} engines: {node: '>=0.10.0'} dev: true + /require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + dev: true + /require-main-filename@2.0.0: resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==} dev: true + /resolve-alpn@1.2.1: + resolution: {integrity: sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==} + dev: true + /resolve-from@4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} @@ -5967,6 +7756,13 @@ packages: supports-preserve-symlinks-flag: 1.0.0 dev: true + /responselike@3.0.0: + resolution: {integrity: sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg==} + engines: {node: '>=14.16'} + dependencies: + lowercase-keys: 3.0.0 + dev: true + /restore-cursor@3.1.0: resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==} engines: {node: '>=8'} @@ -5975,6 +7771,14 @@ packages: signal-exit: 3.0.7 dev: true + /restore-cursor@4.0.0: + resolution: {integrity: sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + onetime: 5.1.2 + signal-exit: 3.0.7 + dev: true + /reusify@1.0.4: resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} @@ -6035,6 +7839,11 @@ packages: engines: {node: '>=0.12.0'} dev: true + /run-async@3.0.0: + resolution: {integrity: sha512-540WwVDOMxA6dN6We19EcT9sc3hkXPw5mzRNGM3FkdN/vtE9NFvj5lFAPNwUDmJjXidm3v7TC1cTE7t17Ulm1Q==} + engines: {node: '>=0.12.0'} + dev: true + /run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} dependencies: @@ -6054,6 +7863,13 @@ packages: tslib: 2.6.2 dev: true + /sade@1.8.1: + resolution: {integrity: sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==} + engines: {node: '>=6'} + dependencies: + mri: 1.2.0 + dev: true + /safe-array-concat@1.0.1: resolution: {integrity: sha512-6XbUAseYE2KtOuGueyeobCySj9L4+66Tn6KQMOPQJrAJEowYKW/YR/MGJZl7FdydUdaFu4LYyDZjxf4/Nmo23Q==} engines: {node: '>=0.4'} @@ -6080,6 +7896,18 @@ packages: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} dev: true + /sax@1.3.0: + resolution: {integrity: sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA==} + dev: true + + /section-matter@1.0.0: + resolution: {integrity: sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==} + engines: {node: '>=4'} + dependencies: + extend-shallow: 2.0.1 + kind-of: 6.0.3 + dev: true + /selfsigned@2.1.1: resolution: {integrity: sha512-GSL3aowiF7wa/WtSFwnUrludWFoNhftq8bUkH9pkzjpN2XSPOAYEgg6e0sS9s0rZwgJzJiQRPU18A6clnoW5wQ==} engines: {node: '>=10'} @@ -6166,6 +7994,21 @@ packages: resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} dev: true + /sharp@0.32.6: + resolution: {integrity: sha512-KyLTWwgcR9Oe4d9HwCwNM2l7+J0dUQwn/yf7S0EnTtb0eVS4RxO0eUSvxPtzT4F3SY+C4K6fqdv/DO27sJ/v/w==} + engines: {node: '>=14.15.0'} + requiresBuild: true + dependencies: + color: 4.2.3 + detect-libc: 2.0.2 + node-addon-api: 6.1.0 + prebuild-install: 7.1.1 + semver: 7.5.4 + simple-get: 4.0.1 + tar-fs: 3.0.4 + tunnel-agent: 0.6.0 + dev: true + /shebang-command@1.2.0: resolution: {integrity: sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==} engines: {node: '>=0.10.0'} @@ -6205,6 +8048,24 @@ packages: resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} dev: true + /simple-concat@1.0.1: + resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==} + dev: true + + /simple-get@4.0.1: + resolution: {integrity: sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==} + dependencies: + decompress-response: 6.0.0 + once: 1.4.0 + simple-concat: 1.0.1 + dev: true + + /simple-swizzle@0.2.2: + resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==} + dependencies: + is-arrayish: 0.3.2 + dev: true + /slash@3.0.0: resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} engines: {node: '>=8'} @@ -6239,6 +8100,42 @@ packages: no-case: 2.3.2 dev: true + /socket.io-adapter@2.5.2: + resolution: {integrity: sha512-87C3LO/NOMc+eMcpcxUBebGjkpMDkNBS9tf7KJqcDsmL936EChtVva71Dw2q4tQcuVC+hAUy4an2NO/sYXmwRA==} + dependencies: + ws: 8.11.0 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + dev: true + + /socket.io-parser@4.2.4: + resolution: {integrity: sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==} + engines: {node: '>=10.0.0'} + dependencies: + '@socket.io/component-emitter': 3.1.0 + debug: 4.3.4 + transitivePeerDependencies: + - supports-color + dev: true + + /socket.io@4.7.2: + resolution: {integrity: sha512-bvKVS29/I5fl2FGLNHuXlQaUH/BlzX1IN6S+NKLNZpBsPZIDH+90eQmCs2Railn4YUiww4SzUedJ6+uzwFnKLw==} + engines: {node: '>=10.2.0'} + dependencies: + accepts: 1.3.8 + base64id: 2.0.0 + cors: 2.8.5 + debug: 4.3.4 + engine.io: 6.5.4 + socket.io-adapter: 2.5.2 + socket.io-parser: 4.2.4 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + dev: true + /socks-proxy-agent@8.0.2: resolution: {integrity: sha512-8zuqoLv1aP/66PHF5TqwJ7Czm3Yv32urJQHrVyhD7mmA6d61Zv8cIXQYPTWwmg6qlupnPvs/QKDmfa4P/qct2g==} engines: {node: '>= 14'} @@ -6359,6 +8256,13 @@ packages: /std-env@3.4.3: resolution: {integrity: sha512-f9aPhy8fYBuMN+sNfakZV18U39PbalgjXG3lLB9WkaYTxijru61wb57V9wxxNthXM5Sd88ETBWi29qLAsHO52Q==} + /stdin-discarder@0.1.0: + resolution: {integrity: sha512-xhV7w8S+bUwlPTb4bAOUQhv8/cSS5offJuX8GQGq32ONF0ZtDWKfkdomM3HMRA+LhX6um/FZ0COqlwsjD53LeQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + bl: 5.1.0 + dev: true + /stoppable@1.1.0: resolution: {integrity: sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw==} engines: {node: '>=4', npm: '>=6'} @@ -6375,6 +8279,13 @@ packages: engines: {node: '>=10.0.0'} dev: true + /streamx@2.15.5: + resolution: {integrity: sha512-9thPGMkKC2GctCzyCUjME3yR03x2xNo0GPKGkRw2UMYN+gqWa9uqpyNWhmsNCutU5zHmkUum0LsCRQTXUgUCAg==} + dependencies: + fast-fifo: 1.3.2 + queue-tick: 1.0.1 + dev: true + /strict-event-emitter@0.2.8: resolution: {integrity: sha512-KDf/ujU8Zud3YaLtMCcTI4xkZlZVIYxTLr+XIULexP+77EEVWixeXroLUXQXiVtH4XH2W7jr/3PT1v3zBuvc3A==} dependencies: @@ -6439,6 +8350,13 @@ packages: safe-buffer: 5.2.1 dev: true + /stringify-entities@4.0.3: + resolution: {integrity: sha512-BP9nNHMhhfcMbiuQKCqMjhDP5yBCAxsPu4pHFFzJ6Alo9dZgY4VLDPutXqIjpRiMoKdp7Av85Gr73Q5uH9k7+g==} + dependencies: + character-entities-html4: 2.1.0 + character-entities-legacy: 3.0.0 + dev: true + /strip-ansi@6.0.1: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} @@ -6446,6 +8364,18 @@ packages: ansi-regex: 5.0.1 dev: true + /strip-ansi@7.1.0: + resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} + engines: {node: '>=12'} + dependencies: + ansi-regex: 6.0.1 + dev: true + + /strip-bom-string@1.0.0: + resolution: {integrity: sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==} + engines: {node: '>=0.10.0'} + dev: true + /strip-bom@3.0.0: resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} engines: {node: '>=4'} @@ -6536,6 +8466,54 @@ packages: engines: {node: '>=6'} dev: true + /tar-fs@2.1.1: + resolution: {integrity: sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==} + dependencies: + chownr: 1.1.4 + mkdirp-classic: 0.5.3 + pump: 3.0.0 + tar-stream: 2.2.0 + dev: true + + /tar-fs@3.0.4: + resolution: {integrity: sha512-5AFQU8b9qLfZCX9zp2duONhPmZv0hGYiBPJsyUdqMjzq/mqVpy/rEUSeHk1+YitmxugaptgBh5oDGU3VsAJq4w==} + dependencies: + mkdirp-classic: 0.5.3 + pump: 3.0.0 + tar-stream: 3.1.6 + dev: true + + /tar-stream@2.2.0: + resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==} + engines: {node: '>=6'} + dependencies: + bl: 4.1.0 + end-of-stream: 1.4.4 + fs-constants: 1.0.0 + inherits: 2.0.4 + readable-stream: 3.6.2 + dev: true + + /tar-stream@3.1.6: + resolution: {integrity: sha512-B/UyjYwPpMBv+PaFSWAmtYjwdrlEaZQEhMIBFNC5oEG8lpiW8XjcSdmEaClj28ArfKScKHs2nshz3k2le6crsg==} + dependencies: + b4a: 1.6.4 + fast-fifo: 1.3.2 + streamx: 2.15.5 + dev: true + + /tar@6.2.0: + resolution: {integrity: sha512-/Wo7DcT0u5HUV486xg675HtjNd3BXZ6xDbzsCUZPt5iw8bTQ63bP0Raut3mvro9u+CUyq7YQd8Cx55fsZXxqLQ==} + engines: {node: '>=10'} + dependencies: + chownr: 2.0.0 + fs-minipass: 2.1.0 + minipass: 5.0.0 + minizlib: 2.1.2 + mkdirp: 1.0.4 + yallist: 4.0.0 + dev: true + /term-size@2.2.1: resolution: {integrity: sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==} engines: {node: '>=8'} @@ -6649,6 +8627,10 @@ packages: engines: {node: '>=8'} dev: true + /trough@2.1.0: + resolution: {integrity: sha512-AqTiAOLcj85xS7vQ8QkAV41hPDIJ71XJB4RCUrzo/1GM2CQwhkJGaf9Hgr7BOugMRpgGUrqRg/DrBDl4H40+8g==} + dev: true + /ts-api-utils@1.0.3(typescript@5.2.2): resolution: {integrity: sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==} engines: {node: '>=16.13.0'} @@ -6770,6 +8752,12 @@ packages: yargs: 17.7.2 dev: true + /tunnel-agent@0.6.0: + resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==} + dependencies: + safe-buffer: 5.2.1 + dev: true + /turbo-darwin-64@1.10.15: resolution: {integrity: sha512-Sik5uogjkRTe1XVP9TC2GryEMOJCaKE2pM/O9uLn4koQDnWKGcLQv+mDU+H+9DXvKLnJnKCD18OVRkwK5tdpoA==} cpu: [x64] @@ -6950,6 +8938,62 @@ packages: busboy: 1.6.0 dev: true + /unified@10.1.2: + resolution: {integrity: sha512-pUSWAi/RAnVy1Pif2kAoeWNBa3JVrx0MId2LASj8G+7AiHWoKZNTomq6LG326T68U7/e263X6fTdcXIy7XnF7Q==} + dependencies: + '@types/unist': 2.0.10 + bail: 2.0.2 + extend: 3.0.2 + is-buffer: 2.0.5 + is-plain-obj: 4.1.0 + trough: 2.1.0 + vfile: 5.3.7 + dev: true + + /unist-util-is@5.2.1: + resolution: {integrity: sha512-u9njyyfEh43npf1M+yGKDGVPbY/JWEemg5nH05ncKPfi+kBbKBJoTdsogMu33uhytuLlv9y0O7GH7fEdwLdLQw==} + dependencies: + '@types/unist': 2.0.10 + dev: true + + /unist-util-position-from-estree@1.1.2: + resolution: {integrity: sha512-poZa0eXpS+/XpoQwGwl79UUdea4ol2ZuCYguVaJS4qzIOMDzbqz8a3erUCOmubSZkaOuGamb3tX790iwOIROww==} + dependencies: + '@types/unist': 2.0.10 + dev: true + + /unist-util-remove-position@4.0.2: + resolution: {integrity: sha512-TkBb0HABNmxzAcfLf4qsIbFbaPDvMO6wa3b3j4VcEzFVaw1LBKwnW4/sRJ/atSLSzoIg41JWEdnE7N6DIhGDGQ==} + dependencies: + '@types/unist': 2.0.10 + unist-util-visit: 4.1.2 + dev: true + + /unist-util-stringify-position@3.0.3: + resolution: {integrity: sha512-k5GzIBZ/QatR8N5X2y+drfpWG8IDBzdnVj6OInRNWm1oXrzydiaAT2OQiA8DPRRZyAKb9b6I2a6PxYklZD0gKg==} + dependencies: + '@types/unist': 2.0.10 + dev: true + + /unist-util-visit-parents@5.1.3: + resolution: {integrity: sha512-x6+y8g7wWMyQhL1iZfhIPhDAs7Xwbn9nRosDXl7qoPTSCy0yNxnKc+hWokFifWQIDGi154rdUqKvbCa4+1kLhg==} + dependencies: + '@types/unist': 2.0.10 + unist-util-is: 5.2.1 + dev: true + + /unist-util-visit@4.1.2: + resolution: {integrity: sha512-MSd8OUGISqHdVvfY9TPhyK2VdUrPgxkUtWSuMHF6XAAFuL4LokseigBnZtPnJMu+FbynTkFNnFlyjxpVKujMRg==} + dependencies: + '@types/unist': 2.0.10 + unist-util-is: 5.2.1 + unist-util-visit-parents: 5.1.3 + dev: true + + /universal-user-agent@6.0.1: + resolution: {integrity: sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ==} + dev: true + /universalify@0.1.2: resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} engines: {node: '>= 4.0.0'} @@ -7023,6 +9067,17 @@ packages: engines: {node: '>= 0.4.0'} dev: true + /uvu@0.5.6: + resolution: {integrity: sha512-+g8ENReyr8YsOc6fv/NVJs2vFdHBnBNdfE49rshrTzDWOlUx4Gq7KOS2GD8eqhy2j+Ejq29+SbKH8yjkAqXqoA==} + engines: {node: '>=8'} + hasBin: true + dependencies: + dequal: 2.0.3 + diff: 5.1.0 + kleur: 4.1.5 + sade: 1.8.1 + dev: true + /v8-compile-cache-lib@3.0.1: resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} dev: true @@ -7055,6 +9110,22 @@ packages: engines: {node: '>= 0.8'} dev: true + /vfile-message@3.1.4: + resolution: {integrity: sha512-fa0Z6P8HUrQN4BZaX05SIVXic+7kE3b05PWAtPuYP9QLHsLKYR7/AlLW3NtOrpXRLeawpDLMsVkmk5DG0NXgWw==} + dependencies: + '@types/unist': 2.0.10 + unist-util-stringify-position: 3.0.3 + dev: true + + /vfile@5.3.7: + resolution: {integrity: sha512-r7qlzkgErKjobAmyNIkkSpizsFPYiUPuJb5pNW1RB4JcYVZhs4lIbVqk8XPk033CV/1z8ss5pkax8SuhGpcG8g==} + dependencies: + '@types/unist': 2.0.10 + is-buffer: 2.0.5 + unist-util-stringify-position: 3.0.3 + vfile-message: 3.1.4 + dev: true + /vite-node@0.34.5(@types/node@18.18.0): resolution: {integrity: sha512-RNZ+DwbCvDoI5CbCSQSyRyzDTfFvFauvMs6Yq4ObJROKlIKuat1KgSX/Ako5rlDMfVCyMcpMRMTkJBxd6z8YRA==} engines: {node: '>=v14.18.0'} @@ -7359,6 +9430,19 @@ packages: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} dev: true + /ws@8.11.0: + resolution: {integrity: sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: ^5.0.2 + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + dev: true + /ws@8.14.2: resolution: {integrity: sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g==} engines: {node: '>=10.0.0'} @@ -7372,6 +9456,19 @@ packages: optional: true dev: true + /xml2js@0.6.2: + resolution: {integrity: sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==} + engines: {node: '>=4.0.0'} + dependencies: + sax: 1.3.0 + xmlbuilder: 11.0.1 + dev: true + + /xmlbuilder@11.0.1: + resolution: {integrity: sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==} + engines: {node: '>=4.0'} + dev: true + /xxhash-wasm@1.0.2: resolution: {integrity: sha512-ibF0Or+FivM9lNrg+HGJfVX8WJqgo+kCLDc4vx6xMeTce7Aj+DLttKbxxRR/gNLSAelRc1omAPlJ77N/Jem07A==} dev: true @@ -7467,5 +9564,17 @@ packages: stacktracey: 2.1.8 dev: true + /zod-to-json-schema@3.21.4(zod@3.22.2): + resolution: {integrity: sha512-fjUZh4nQ1s6HMccgIeE0VP4QG/YRGPmyjO9sAh890aQKPEk3nqbfUXhMFaC+Dr5KvYBm8BCyvfpZf2jY9aGSsw==} + peerDependencies: + zod: ^3.21.4 + dependencies: + zod: 3.22.2 + dev: true + /zod@3.22.2: resolution: {integrity: sha512-wvWkphh5WQsJbVk1tbx1l1Ly4yg+XecD+Mq280uBGt9wa5BKSWf4Mhp6GmrkPixhMxmabYY7RbzlwVP32pbGCg==} + + /zwitch@2.0.4: + resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} + dev: true From ebb4275a77c691181fc438b12970faab27beafbd Mon Sep 17 00:00:00 2001 From: Kerem Kazan Date: Tue, 14 Nov 2023 07:50:49 -0500 Subject: [PATCH 34/41] added console.info for request handler --- .../lib/request-handler/abstract-request-handler.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/nexus/src/lib/request-handler/abstract-request-handler.ts b/packages/nexus/src/lib/request-handler/abstract-request-handler.ts index a46558b..277a2c9 100644 --- a/packages/nexus/src/lib/request-handler/abstract-request-handler.ts +++ b/packages/nexus/src/lib/request-handler/abstract-request-handler.ts @@ -11,10 +11,18 @@ export abstract class AbstractRequestHandler { constructor(protected readonly nexus: Nexus) {} public async handle(): Promise { + console.info("building context..."); const context = await this.getContext(); + + console.info("context built. getting pre-response..."); const preResponse = await this.getPreResponseFromContext(context); - return this.handlePreResponse(preResponse); + console.info("pre-response received. preparing final response..."); + const response = this.handlePreResponse(preResponse); + + console.info("final response prepared. returning response..."); + + return response; } protected async getPreResponseFromContext( From 08abccbd341aedd4e6574341bf547e3ada17ec17 Mon Sep 17 00:00:00 2001 From: Kerem Kazan Date: Wed, 15 Nov 2023 08:43:04 -0500 Subject: [PATCH 35/41] using whatwg-node server adapter --- examples/cloudflare-worker/src/index.ts | 9 +-- examples/cloudflare-worker/tsconfig.json | 3 +- packages/nexus/package.json | 3 + packages/nexus/src/index.ts | 12 ++++ packages/nexus/src/lib/nexus/nexus.ts | 15 +++++ pnpm-lock.yaml | 77 +++++++++++++++++++++--- 6 files changed, 103 insertions(+), 16 deletions(-) create mode 100644 packages/nexus/src/index.ts diff --git a/examples/cloudflare-worker/src/index.ts b/examples/cloudflare-worker/src/index.ts index 5fbfb02..af541ca 100644 --- a/examples/cloudflare-worker/src/index.ts +++ b/examples/cloudflare-worker/src/index.ts @@ -1,19 +1,16 @@ import { Nexus } from "@whatsgood/nexus"; -import { RequestHandler } from "@whatsgood/nexus/fetch"; // TODO: add config alerts to indicate that the key access is incomplete // TODO: add onboarding & UX. (setup admin access, login, etc) // TODO: add tests for the worker +const server = Nexus.createServer(); + export default { async fetch( request: Request, env: Record ): Promise { - const nexus = new Nexus({ - env, - }); - const requestHandler = new RequestHandler(nexus, request); - return requestHandler.handle(); + return server.fetch(request, env); }, }; diff --git a/examples/cloudflare-worker/tsconfig.json b/examples/cloudflare-worker/tsconfig.json index c4e191e..173b830 100644 --- a/examples/cloudflare-worker/tsconfig.json +++ b/examples/cloudflare-worker/tsconfig.json @@ -7,6 +7,7 @@ "types": ["@cloudflare/workers-types"], "moduleResolution": "NodeNext", "module": "NodeNext", - "noEmit": true + "noEmit": true, + "skipLibCheck": true } } diff --git a/packages/nexus/package.json b/packages/nexus/package.json index ddba79e..ee2df05 100644 --- a/packages/nexus/package.json +++ b/packages/nexus/package.json @@ -40,6 +40,7 @@ "license": "MIT", "scripts": { "build": "tsup", + "dev": "ts-node -r tsconfig-paths/register src/index.ts", "prepublish": "turbo build --filter=@whatsgood/nexus", "typecheck": "tsc", "lint": "eslint src/", @@ -49,6 +50,8 @@ "test:unit:coverage": "pnpm test:unit --coverage" }, "dependencies": { + "@whatwg-node/server": "^0.9.16", + "@whatwg-node/fetch": "0.9.14", "path-to-regexp": "^6.2.1", "vitest": "0.34.5", "zod": "^3.22.2" diff --git a/packages/nexus/src/index.ts b/packages/nexus/src/index.ts new file mode 100644 index 0000000..17eaab1 --- /dev/null +++ b/packages/nexus/src/index.ts @@ -0,0 +1,12 @@ +// import { createServer } from "node:http"; +// import myServerAdapter from "./my-server-adapter"; + +// const nodeServer = createServer((req, res) => { +// myServerAdapter(req, res).catch((err) => { +// console.log(err); +// }); +// }); + +// nodeServer.listen(4005, () => { +// console.log("Server listening on port 4005"); +// }); diff --git a/packages/nexus/src/lib/nexus/nexus.ts b/packages/nexus/src/lib/nexus/nexus.ts index 0bcbbf4..960bac2 100644 --- a/packages/nexus/src/lib/nexus/nexus.ts +++ b/packages/nexus/src/lib/nexus/nexus.ts @@ -1,3 +1,5 @@ +import { createServerAdapter } from "@whatwg-node/server"; +import { RequestHandler } from "src/fetch"; import type { ChainRegistry } from "../chain/chain-registry"; import { Config } from "../config"; import type { ConfigConstructorParams } from "../config"; @@ -30,4 +32,17 @@ export class Nexus { serviceProviderRegistry: this.serviceProviderRegistry, }); } + + public static createServer() { + return createServerAdapter( + (request: Request, env: Record) => { + const nexus = new Nexus({ + env, + }); + const requestHandler = new RequestHandler(nexus, request); + + return requestHandler.handle(); + } + ); + } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index cc77c99..be3935d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -47,13 +47,13 @@ importers: devDependencies: '@cloudflare/workers-types': specifier: ^4.20230419.0 - version: 4.20230419.0 + version: 4.20230518.0 typescript: specifier: 5.2.2 version: 5.2.2 wrangler: specifier: ^3.0.0 - version: 3.0.0 + version: 3.1.0 packages/eslint-config-custom: devDependencies: @@ -72,6 +72,12 @@ importers: packages/nexus: dependencies: + '@whatwg-node/fetch': + specifier: 0.9.14 + version: 0.9.14 + '@whatwg-node/server': + specifier: ^0.9.16 + version: 0.9.16 path-to-regexp: specifier: ^6.2.1 version: 6.2.1 @@ -618,8 +624,8 @@ packages: dev: true optional: true - /@cloudflare/workers-types@4.20230419.0: - resolution: {integrity: sha512-MfNBlHrI/ekRkbLtdAo23D4hkXF+3QD92OCwuXxCUK73HtMHuBqkMp9T/8KFbKNRCnz7PzUderc7Jr5m3eeW3g==} + /@cloudflare/workers-types@4.20230518.0: + resolution: {integrity: sha512-A0w1V+5SUawGaaPRlhFhSC/SCDT9oQG8TMoWOKFLA4qbqagELqEAFD4KySBIkeVOvCBLT1DZSYBMCxbXddl0kw==} dev: true /@cspotcode/source-map-support@0.8.1: @@ -2131,6 +2137,38 @@ packages: loupe: 2.3.6 pretty-format: 29.7.0 + /@whatwg-node/events@0.1.1: + resolution: {integrity: sha512-AyQEn5hIPV7Ze+xFoXVU3QTHXVbWPrzaOkxtENMPMuNL6VVHrp4hHfDt9nrQpjO7BgvuM95dMtkycX5M/DZR3w==} + engines: {node: '>=16.0.0'} + dev: false + + /@whatwg-node/fetch@0.9.14: + resolution: {integrity: sha512-wurZC82zzZwXRDSW0OS9l141DynaJQh7Yt0FD1xZ8niX7/Et/7RoiLiltbVU1fSF1RR9z6ndEaTUQBAmddTm1w==} + engines: {node: '>=16.0.0'} + dependencies: + '@whatwg-node/node-fetch': 0.5.0 + urlpattern-polyfill: 9.0.0 + dev: false + + /@whatwg-node/node-fetch@0.5.0: + resolution: {integrity: sha512-q76lDAafvHNGWedNAVHrz/EyYTS8qwRLcwne8SJQdRN5P3HydxU6XROFvJfTML6KZXQX2FDdGY4/SnaNyd7M0Q==} + engines: {node: '>=16.0.0'} + dependencies: + '@whatwg-node/events': 0.1.1 + busboy: 1.6.0 + fast-querystring: 1.1.2 + fast-url-parser: 1.1.3 + tslib: 2.6.2 + dev: false + + /@whatwg-node/server@0.9.16: + resolution: {integrity: sha512-gktQkRyONEw2EGpx7UZaC6zNlUm21CGlqAHQXU3QC6W0zlLM5ZQNDCeD66q/nsPHDV08X2NTHlABsuAEk5rh/w==} + engines: {node: '>=16.0.0'} + dependencies: + '@whatwg-node/fetch': 0.9.14 + tslib: 2.6.2 + dev: false + /@xmldom/xmldom@0.8.10: resolution: {integrity: sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==} engines: {node: '>=10.0.0'} @@ -2622,7 +2660,6 @@ packages: engines: {node: '>=10.16.0'} dependencies: streamsearch: 1.1.0 - dev: true /bytes@3.1.2: resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} @@ -4377,6 +4414,10 @@ packages: tmp: 0.0.33 dev: true + /fast-decode-uri-component@1.0.1: + resolution: {integrity: sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==} + dev: false + /fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} dev: true @@ -4404,6 +4445,18 @@ packages: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} dev: true + /fast-querystring@1.1.2: + resolution: {integrity: sha512-g6KuKWmFXc0fID8WWH0jit4g0AGBoJhCkJMb1RmbsSEUNvQ+ZC8D6CUZ+GtF8nMzSPXnhiePyyqqipzNNEnHjg==} + dependencies: + fast-decode-uri-component: 1.0.1 + dev: false + + /fast-url-parser@1.1.3: + resolution: {integrity: sha512-5jOCVXADYNuRkKFzNJ0dCCewsZiYo0dz8QNYljkOpFC6r2U4OBmKtvm/Tsuh4w1YYdDqDb31a8TVhBJ2OJKdqQ==} + dependencies: + punycode: 1.4.1 + dev: false + /fastq@1.15.0: resolution: {integrity: sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==} dependencies: @@ -7466,6 +7519,10 @@ packages: once: 1.4.0 dev: true + /punycode@1.4.1: + resolution: {integrity: sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==} + dev: false + /punycode@2.3.0: resolution: {integrity: sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==} engines: {node: '>=6'} @@ -8277,7 +8334,6 @@ packages: /streamsearch@1.1.0: resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} engines: {node: '>=10.0.0'} - dev: true /streamx@2.15.5: resolution: {integrity: sha512-9thPGMkKC2GctCzyCUjME3yR03x2xNo0GPKGkRw2UMYN+gqWa9uqpyNWhmsNCutU5zHmkUum0LsCRQTXUgUCAg==} @@ -8690,7 +8746,6 @@ packages: /tslib@2.6.2: resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} - dev: true /tsup@6.1.3(ts-node@10.9.1)(typescript@5.2.2): resolution: {integrity: sha512-eRpBnbfpDFng+EJNTQ90N7QAf4HAGGC7O3buHIjroKWK7D1ibk9/YnR/3cS8HsMU5T+6Oi+cnF+yU5WmCnB//Q==} @@ -9048,6 +9103,10 @@ packages: punycode: 2.3.0 dev: true + /urlpattern-polyfill@9.0.0: + resolution: {integrity: sha512-WHN8KDQblxd32odxeIgo83rdVDE2bvdkb86it7bMhYZwWKJz0+O0RK/eZiHYnM+zgt/U7hAHOlCQGfjjvSkw2g==} + dev: false + /util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} dev: true @@ -9383,8 +9442,8 @@ packages: '@cloudflare/workerd-windows-64': 1.20230922.0 dev: true - /wrangler@3.0.0: - resolution: {integrity: sha512-azppXJjEQeaVg3wxFZJOGDGtpR8e9clHr2aHuanGHOk9vX3gvesCv97BF/n8qh1Y1d7vDKOBdfQW3UOYZNFGNw==} + /wrangler@3.1.0: + resolution: {integrity: sha512-oqVBJZoOQqSKxhgaPt4LcmtBf0FssIz/4F1VgjWOomeGQ3kN9pg3swPO0Zkf0aAphDodG9rekjrtccvKW7bSsA==} engines: {node: '>=16.13.0'} hasBin: true dependencies: From 24b3350fa6ff89eff3a0f663515461c82d88d1bd Mon Sep 17 00:00:00 2001 From: Kerem Kazan Date: Wed, 15 Nov 2023 09:09:28 -0500 Subject: [PATCH 36/41] combining request context with constructor configs --- examples/cloudflare-worker/src/index.ts | 17 ++++++++++++++- packages/nexus/src/lib/config.ts | 7 +++++++ packages/nexus/src/lib/nexus/nexus.ts | 21 ++++++++++++------- .../src/lib/rpc-endpoint/rpc-endpoint.ts | 2 ++ .../lib/service-provider/service-provider.ts | 6 ++++++ 5 files changed, 44 insertions(+), 9 deletions(-) diff --git a/examples/cloudflare-worker/src/index.ts b/examples/cloudflare-worker/src/index.ts index af541ca..ae8b836 100644 --- a/examples/cloudflare-worker/src/index.ts +++ b/examples/cloudflare-worker/src/index.ts @@ -4,7 +4,22 @@ import { Nexus } from "@whatsgood/nexus"; // TODO: add onboarding & UX. (setup admin access, login, etc) // TODO: add tests for the worker -const server = Nexus.createServer(); +const server = Nexus.createServer({ + providers: { + // alchemy: { + // disabled: true, + // }, + base: { + disabled: true, + }, + infura: { + disabled: true, + }, + ankr: { + disabled: true, + }, + }, +}); export default { async fetch( diff --git a/packages/nexus/src/lib/config.ts b/packages/nexus/src/lib/config.ts index 7d3c6ca..5bc8611 100644 --- a/packages/nexus/src/lib/config.ts +++ b/packages/nexus/src/lib/config.ts @@ -10,6 +10,7 @@ type RpcRelayRecoveryMode = z.infer; interface ProviderConfig { key?: string; + disabled?: boolean; } type Env = Partial>; @@ -42,14 +43,20 @@ export class Config { this.providers = { ankr: { + ...params.providers?.ankr, key: params.providers?.ankr?.key || env.NEXUS_ANKR_KEY, }, infura: { + ...params.providers?.infura, key: params.providers?.infura?.key || env.NEXUS_INFURA_KEY, }, alchemy: { + ...params.providers?.alchemy, key: params.providers?.alchemy?.key || env.NEXUS_ALCHEMY_KEY, }, + base: { + ...params.providers?.base, + }, }; this.globalAccessKey = diff --git a/packages/nexus/src/lib/nexus/nexus.ts b/packages/nexus/src/lib/nexus/nexus.ts index 960bac2..adeb692 100644 --- a/packages/nexus/src/lib/nexus/nexus.ts +++ b/packages/nexus/src/lib/nexus/nexus.ts @@ -10,18 +10,18 @@ import { defaultServiceProviderRegistry, } from "../setup/data"; +interface NexusConstructorParams extends ConfigConstructorParams { + chainRegistry?: ChainRegistry; + serviceProviderRegistry?: ServiceProviderRegistry; +} + export class Nexus { public readonly config: Config; public readonly serviceProviderRegistry: ServiceProviderRegistry; public readonly chainRegistry: ChainRegistry; public readonly rpcEndpointPoolFactory: RpcEndpointPoolFactory; - constructor( - params: ConfigConstructorParams & { - chainRegistry?: ChainRegistry; - serviceProviderRegistry?: ServiceProviderRegistry; - } = {} - ) { + private constructor(params: NexusConstructorParams = {}) { this.config = new Config(params); this.chainRegistry = params.chainRegistry ?? defaultChainRegistry; this.serviceProviderRegistry = @@ -33,11 +33,16 @@ export class Nexus { }); } - public static createServer() { + public static createServer(params: NexusConstructorParams = {}) { + // TODO: add process.env for node adapters return createServerAdapter( (request: Request, env: Record) => { const nexus = new Nexus({ - env, + env: { + ...params.env, + ...env, + }, + providers: params.providers, }); const requestHandler = new RequestHandler(nexus, request); diff --git a/packages/nexus/src/lib/rpc-endpoint/rpc-endpoint.ts b/packages/nexus/src/lib/rpc-endpoint/rpc-endpoint.ts index 73c7def..def1905 100644 --- a/packages/nexus/src/lib/rpc-endpoint/rpc-endpoint.ts +++ b/packages/nexus/src/lib/rpc-endpoint/rpc-endpoint.ts @@ -54,6 +54,8 @@ export class RpcEndpoint { }, }); + console.info(`Attempting relay to Provider: ${this.provider.name}`); + let relayResponse: Response; try { diff --git a/packages/nexus/src/lib/service-provider/service-provider.ts b/packages/nexus/src/lib/service-provider/service-provider.ts index 1a131d3..dfec1d3 100644 --- a/packages/nexus/src/lib/service-provider/service-provider.ts +++ b/packages/nexus/src/lib/service-provider/service-provider.ts @@ -37,6 +37,12 @@ export class ServiceProvider { return undefined; } + if (config.providers[this.name]?.disabled) { + console.warn(`Service provider: ${this.name} is disabled`); + + return undefined; + } + if (chainSupport.type === "url-append-key") { const key = config.providers[this.name]?.key; From 30094add2bfa89ae2b185538fd6bffe29752df5a Mon Sep 17 00:00:00 2001 From: Kerem Kazan Date: Wed, 15 Nov 2023 09:14:07 -0500 Subject: [PATCH 37/41] changesets --- .changeset/early-apricots-serve.md | 5 +++++ .changeset/good-bottles-joke.md | 5 +++++ .changeset/little-yaks-drive.md | 5 +++++ .changeset/many-ligers-repeat.md | 5 +++++ .changeset/spotty-mangos-smile.md | 6 ++++++ .changeset/tender-flowers-notice.md | 5 +++++ 6 files changed, 31 insertions(+) create mode 100644 .changeset/early-apricots-serve.md create mode 100644 .changeset/good-bottles-joke.md create mode 100644 .changeset/little-yaks-drive.md create mode 100644 .changeset/many-ligers-repeat.md create mode 100644 .changeset/spotty-mangos-smile.md create mode 100644 .changeset/tender-flowers-notice.md diff --git a/.changeset/early-apricots-serve.md b/.changeset/early-apricots-serve.md new file mode 100644 index 0000000..32688d9 --- /dev/null +++ b/.changeset/early-apricots-serve.md @@ -0,0 +1,5 @@ +--- +"@whatsgood/nexus": patch +--- + +multi-entrypoint builds added diff --git a/.changeset/good-bottles-joke.md b/.changeset/good-bottles-joke.md new file mode 100644 index 0000000..72ffa8c --- /dev/null +++ b/.changeset/good-bottles-joke.md @@ -0,0 +1,5 @@ +--- +"@whatsgood/nexus": patch +--- + +introduced AbstractRequestHandler for cross-platform extensions diff --git a/.changeset/little-yaks-drive.md b/.changeset/little-yaks-drive.md new file mode 100644 index 0000000..2b8e4bb --- /dev/null +++ b/.changeset/little-yaks-drive.md @@ -0,0 +1,5 @@ +--- +"@whatsgood/nexus": patch +--- + +Introducing esm builds diff --git a/.changeset/many-ligers-repeat.md b/.changeset/many-ligers-repeat.md new file mode 100644 index 0000000..0c6e797 --- /dev/null +++ b/.changeset/many-ligers-repeat.md @@ -0,0 +1,5 @@ +--- +"@whatsgood/nexus": patch +--- + +Nexus class created as main library and config object diff --git a/.changeset/spotty-mangos-smile.md b/.changeset/spotty-mangos-smile.md new file mode 100644 index 0000000..76676b1 --- /dev/null +++ b/.changeset/spotty-mangos-smile.md @@ -0,0 +1,6 @@ +--- +"@whatsgood/example-cloudflare-worker": patch +"@whatsgood/nexus": patch +--- + +whatwg-node integrated for cross-platform support diff --git a/.changeset/tender-flowers-notice.md b/.changeset/tender-flowers-notice.md new file mode 100644 index 0000000..eb5c2da --- /dev/null +++ b/.changeset/tender-flowers-notice.md @@ -0,0 +1,5 @@ +--- +"@whatsgood/nexus": patch +--- + +Config object can now disable providers From 9348a06cdbdbe02f0fd34d67f0cae61d9a2fdce5 Mon Sep 17 00:00:00 2001 From: Kerem Kazan Date: Wed, 15 Nov 2023 09:16:25 -0500 Subject: [PATCH 38/41] removed whatwg-node/fetch --- packages/nexus/package.json | 1 - pnpm-lock.yaml | 3 --- 2 files changed, 4 deletions(-) diff --git a/packages/nexus/package.json b/packages/nexus/package.json index ee2df05..abafacd 100644 --- a/packages/nexus/package.json +++ b/packages/nexus/package.json @@ -51,7 +51,6 @@ }, "dependencies": { "@whatwg-node/server": "^0.9.16", - "@whatwg-node/fetch": "0.9.14", "path-to-regexp": "^6.2.1", "vitest": "0.34.5", "zod": "^3.22.2" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index be3935d..b41a831 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -72,9 +72,6 @@ importers: packages/nexus: dependencies: - '@whatwg-node/fetch': - specifier: 0.9.14 - version: 0.9.14 '@whatwg-node/server': specifier: ^0.9.16 version: 0.9.16 From 797691e924e447bf2d2483b8078710045a863026 Mon Sep 17 00:00:00 2001 From: Kerem Kazan Date: Wed, 15 Nov 2023 09:17:35 -0500 Subject: [PATCH 39/41] removed old code --- packages/nexus/src/index.ts | 12 ------------ 1 file changed, 12 deletions(-) delete mode 100644 packages/nexus/src/index.ts diff --git a/packages/nexus/src/index.ts b/packages/nexus/src/index.ts deleted file mode 100644 index 17eaab1..0000000 --- a/packages/nexus/src/index.ts +++ /dev/null @@ -1,12 +0,0 @@ -// import { createServer } from "node:http"; -// import myServerAdapter from "./my-server-adapter"; - -// const nodeServer = createServer((req, res) => { -// myServerAdapter(req, res).catch((err) => { -// console.log(err); -// }); -// }); - -// nodeServer.listen(4005, () => { -// console.log("Server listening on port 4005"); -// }); From 578b242fe9ebdb3714d34e9882c566cdd1cf2083 Mon Sep 17 00:00:00 2001 From: Kerem Kazan Date: Wed, 15 Nov 2023 09:19:51 -0500 Subject: [PATCH 40/41] changesets --- .changeset/green-toes-tie.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/green-toes-tie.md diff --git a/.changeset/green-toes-tie.md b/.changeset/green-toes-tie.md new file mode 100644 index 0000000..c38c159 --- /dev/null +++ b/.changeset/green-toes-tie.md @@ -0,0 +1,5 @@ +--- +"@whatsgood/nexus": patch +--- + +tsup builds with code splitting From 4ecb8a1174563581ad2c96d365f844ad173fd929 Mon Sep 17 00:00:00 2001 From: Kerem Kazan Date: Wed, 15 Nov 2023 09:22:31 -0500 Subject: [PATCH 41/41] temporarily disabling node fetch handler --- packages/nexus/src/lib/nexus/nexus.ts | 2 +- packages/nexus/src/node/index.ts | 6 +- .../nexus/src/node/request-handler/index.ts | 2 +- .../node/request-handler/request-handler.ts | 366 +++++++++--------- 4 files changed, 188 insertions(+), 188 deletions(-) diff --git a/packages/nexus/src/lib/nexus/nexus.ts b/packages/nexus/src/lib/nexus/nexus.ts index adeb692..904c153 100644 --- a/packages/nexus/src/lib/nexus/nexus.ts +++ b/packages/nexus/src/lib/nexus/nexus.ts @@ -21,7 +21,7 @@ export class Nexus { public readonly chainRegistry: ChainRegistry; public readonly rpcEndpointPoolFactory: RpcEndpointPoolFactory; - private constructor(params: NexusConstructorParams = {}) { + constructor(params: NexusConstructorParams = {}) { this.config = new Config(params); this.chainRegistry = params.chainRegistry ?? defaultChainRegistry; this.serviceProviderRegistry = diff --git a/packages/nexus/src/node/index.ts b/packages/nexus/src/node/index.ts index 7ca401b..1289473 100644 --- a/packages/nexus/src/node/index.ts +++ b/packages/nexus/src/node/index.ts @@ -1,4 +1,4 @@ -// TODO: how can i let the user specify the load balancing weights per service provider or even per custom provider url? -// TODO: how do i build fallback logic? for example, `if ankr fails, fallback to infura`? +// // TODO: how can i let the user specify the load balancing weights per service provider or even per custom provider url? +// // TODO: how do i build fallback logic? for example, `if ankr fails, fallback to infura`? -export { RequestHandler } from "./request-handler"; +// export { RequestHandler } from "./request-handler"; diff --git a/packages/nexus/src/node/request-handler/index.ts b/packages/nexus/src/node/request-handler/index.ts index 2e1ec7a..f676101 100644 --- a/packages/nexus/src/node/request-handler/index.ts +++ b/packages/nexus/src/node/request-handler/index.ts @@ -1 +1 @@ -export * from "./request-handler"; +// export * from "./request-handler"; diff --git a/packages/nexus/src/node/request-handler/request-handler.ts b/packages/nexus/src/node/request-handler/request-handler.ts index 288465a..15e6b90 100644 --- a/packages/nexus/src/node/request-handler/request-handler.ts +++ b/packages/nexus/src/node/request-handler/request-handler.ts @@ -1,183 +1,183 @@ -import type http from "node:http"; -import NodeURL from "node:url"; -import querystring from "node:querystring"; -import { matchPath } from "@lib/routes"; -import { JsonRPCRequestSchema } from "@lib/rpc-endpoint/json-rpc-types"; -import type { Nexus } from "@lib/nexus"; -import { RpcProxyContext } from "@lib/request-handler/rpc-proxy-context"; -import type { NexusPreResponse } from "@lib/request-handler/abstract-request-handler"; -import { AbstractRequestHandler } from "@lib/request-handler/abstract-request-handler"; - -// TODO: write handler tests for the node handler - -export class RequestHandler extends AbstractRequestHandler { - constructor( - protected readonly nexus: Nexus, - private readonly req: http.IncomingMessage, - private readonly res: http.ServerResponse - ) { - super(nexus); - } - - protected handlePreResponse(preResponse: NexusPreResponse) { - if (preResponse.type === "json") { - this.res.writeHead(preResponse.status, { - "Content-Type": "application/json", - }); - this.res.end(JSON.stringify(preResponse.body)); - } else { - console.error(preResponse); - const message = `Unsupported response type: ${preResponse.type}`; - const error = new Error(message); - - this.handle500(error, message); - } - } - - public handle500(error: unknown, message?: string) { - console.error(error); - this.res.writeHead(500, { - "Content-Type": "application/json", - }); - this.res.end( - JSON.stringify({ message: message ?? "Internal Server Error" }) - ); - } - - private parseJSONFromNodeHttpRequest() { - // TODO: is any the right choice? - // TODO: test - const body: any[] = []; - let receivedSize = 0; - const maxSize = 1e6; // 1 MB, for example - - return new Promise((resolve, reject) => { - this.req.on("data", (chunk) => { - receivedSize += chunk.length; - - if (receivedSize > maxSize) { - reject(new Error("Payload too large")); - this.req.destroy(); - } else { - body.push(chunk); - } - }); - - this.req.on("end", () => { - try { - const parsedBody: unknown = JSON.parse( - Buffer.concat(body).toString() - ); - - resolve(parsedBody); - } catch (error) { - reject(new Error("Invalid JSON")); - } - }); - - this.req.on("error", (error) => { - reject(error); - }); - }); - } - - private async parseJSONRpcRequest() { - let payload: unknown; - - try { - // TODO: cover the cases where the request is not a json rpc request - // TODO: cover the cases where the request is already parsed as a json object - // from a higher middleware - payload = await this.parseJSONFromNodeHttpRequest(); - } catch (error) { - console.error( - JSON.stringify( - { - req: this.req, - error, - }, - null, - 2 - ) - ); - - return { - type: "invalid-json-request", - req: this.req, - error, - } as const; - } - - try { - const parsedPayload = JsonRPCRequestSchema.safeParse(payload); - - if (!parsedPayload.success) { - console.log("error parsing json rpc request", payload); - console.error(parsedPayload.error); - - return { - type: "invalid-json-rpc-request", - req: this.req, - payload, - error: parsedPayload.error, - } as const; - } - - return { - type: "success", - req: this.req, - data: parsedPayload.data, - } as const; - } catch (error) { - console.error( - JSON.stringify( - { - req: this.req, - error, - }, - null, - 2 - ) - ); - - return { - type: "invalid-json-request", - req: this.req, - error, - } as const; - } - } - - protected async getContext(): Promise { - // TODO: test node http requests - const requestUrl = NodeURL.parse(this.req.url || ""); - const requestPath = requestUrl.pathname; - const { query } = requestUrl; - - const route = requestPath ? matchPath(requestPath) : undefined; - const chain = route - ? this.nexus.chainRegistry.getByOptionalParams(route.params) - : undefined; - const pool = chain - ? this.nexus.rpcEndpointPoolFactory.fromChain(chain) - : undefined; - - const clientAccessKey = query ? querystring.parse(query).key : undefined; - - const jsonRPCRequestParseResult = await this.parseJSONRpcRequest(); - - return new RpcProxyContext({ - pool, - config: this.nexus.config, - httpMethod: this.req.method, - chain, - path: requestPath || "", // TODO: remove this hack, make this more robust - clientAccessKey: - typeof clientAccessKey === "string" ? clientAccessKey : undefined, - jsonRPCRequest: - jsonRPCRequestParseResult.type === "success" - ? jsonRPCRequestParseResult.data - : undefined, - }); - } -} +// import type http from "node:http"; +// import NodeURL from "node:url"; +// import querystring from "node:querystring"; +// import { matchPath } from "@lib/routes"; +// import { JsonRPCRequestSchema } from "@lib/rpc-endpoint/json-rpc-types"; +// import type { Nexus } from "@lib/nexus"; +// import { RpcProxyContext } from "@lib/request-handler/rpc-proxy-context"; +// import type { NexusPreResponse } from "@lib/request-handler/abstract-request-handler"; +// import { AbstractRequestHandler } from "@lib/request-handler/abstract-request-handler"; + +// // TODO: write handler tests for the node handler + +// export class RequestHandler extends AbstractRequestHandler { +// constructor( +// protected readonly nexus: Nexus, +// private readonly req: http.IncomingMessage, +// private readonly res: http.ServerResponse +// ) { +// super(nexus); +// } + +// protected handlePreResponse(preResponse: NexusPreResponse) { +// if (preResponse.type === "json") { +// this.res.writeHead(preResponse.status, { +// "Content-Type": "application/json", +// }); +// this.res.end(JSON.stringify(preResponse.body)); +// } else { +// console.error(preResponse); +// const message = `Unsupported response type: ${preResponse.type}`; +// const error = new Error(message); + +// this.handle500(error, message); +// } +// } + +// public handle500(error: unknown, message?: string) { +// console.error(error); +// this.res.writeHead(500, { +// "Content-Type": "application/json", +// }); +// this.res.end( +// JSON.stringify({ message: message ?? "Internal Server Error" }) +// ); +// } + +// private parseJSONFromNodeHttpRequest() { +// // TODO: is any the right choice? +// // TODO: test +// const body: any[] = []; +// let receivedSize = 0; +// const maxSize = 1e6; // 1 MB, for example + +// return new Promise((resolve, reject) => { +// this.req.on("data", (chunk) => { +// receivedSize += chunk.length; + +// if (receivedSize > maxSize) { +// reject(new Error("Payload too large")); +// this.req.destroy(); +// } else { +// body.push(chunk); +// } +// }); + +// this.req.on("end", () => { +// try { +// const parsedBody: unknown = JSON.parse( +// Buffer.concat(body).toString() +// ); + +// resolve(parsedBody); +// } catch (error) { +// reject(new Error("Invalid JSON")); +// } +// }); + +// this.req.on("error", (error) => { +// reject(error); +// }); +// }); +// } + +// private async parseJSONRpcRequest() { +// let payload: unknown; + +// try { +// // TODO: cover the cases where the request is not a json rpc request +// // TODO: cover the cases where the request is already parsed as a json object +// // from a higher middleware +// payload = await this.parseJSONFromNodeHttpRequest(); +// } catch (error) { +// console.error( +// JSON.stringify( +// { +// req: this.req, +// error, +// }, +// null, +// 2 +// ) +// ); + +// return { +// type: "invalid-json-request", +// req: this.req, +// error, +// } as const; +// } + +// try { +// const parsedPayload = JsonRPCRequestSchema.safeParse(payload); + +// if (!parsedPayload.success) { +// console.log("error parsing json rpc request", payload); +// console.error(parsedPayload.error); + +// return { +// type: "invalid-json-rpc-request", +// req: this.req, +// payload, +// error: parsedPayload.error, +// } as const; +// } + +// return { +// type: "success", +// req: this.req, +// data: parsedPayload.data, +// } as const; +// } catch (error) { +// console.error( +// JSON.stringify( +// { +// req: this.req, +// error, +// }, +// null, +// 2 +// ) +// ); + +// return { +// type: "invalid-json-request", +// req: this.req, +// error, +// } as const; +// } +// } + +// protected async getContext(): Promise { +// // TODO: test node http requests +// const requestUrl = NodeURL.parse(this.req.url || ""); +// const requestPath = requestUrl.pathname; +// const { query } = requestUrl; + +// const route = requestPath ? matchPath(requestPath) : undefined; +// const chain = route +// ? this.nexus.chainRegistry.getByOptionalParams(route.params) +// : undefined; +// const pool = chain +// ? this.nexus.rpcEndpointPoolFactory.fromChain(chain) +// : undefined; + +// const clientAccessKey = query ? querystring.parse(query).key : undefined; + +// const jsonRPCRequestParseResult = await this.parseJSONRpcRequest(); + +// return new RpcProxyContext({ +// pool, +// config: this.nexus.config, +// httpMethod: this.req.method, +// chain, +// path: requestPath || "", // TODO: remove this hack, make this more robust +// clientAccessKey: +// typeof clientAccessKey === "string" ? clientAccessKey : undefined, +// jsonRPCRequest: +// jsonRPCRequestParseResult.type === "success" +// ? jsonRPCRequestParseResult.data +// : undefined, +// }); +// } +// }