From c519b2cf85023b7d38b2e55a77f5b48ea81abf4d Mon Sep 17 00:00:00 2001 From: sevenzing Date: Mon, 13 Apr 2026 17:47:31 +0400 Subject: [PATCH 01/21] Add examples for ensapi endpoints --- apps/ensapi/src/app.ts | 2 +- .../api/explore/name-tokens-api.routes.ts | 7 +- .../explore/registrar-actions-api.routes.ts | 45 +- .../api/explore/registrar-actions-api.ts | 2 + .../handlers/api/meta/realtime-api.routes.ts | 2 - .../handlers/api/meta/status-api.routes.ts | 2 - .../api/resolution/resolution-api.routes.ts | 17 +- apps/ensapi/src/lib/handlers/params.schema.ts | 119 +- docs/docs.ensnode.io/ensapi-openapi.json | 1360 +++++++++++++++-- package.json | 1 + .../src/ensapi/api/name-tokens/examples.ts | 39 + .../api/name-tokens/zod-schemas.test.ts | 9 + .../ensapi/api/registrar-actions/examples.ts | 56 + .../ensapi/api/registrar-actions/serialize.ts | 9 + .../api/registrar-actions/zod-schemas.test.ts | 128 +- .../src/ensapi/api/resolution/examples.ts | 39 + .../ensapi/api/resolution/zod-schemas.test.ts | 32 + .../ensapi/api/shared/pagination/response.ts | 4 +- .../api/shared/pagination/zod-schemas.ts | 2 - packages/ensnode-sdk/src/internal.ts | 3 + pnpm-lock.yaml | 800 +++++----- 21 files changed, 1978 insertions(+), 700 deletions(-) create mode 100644 packages/ensnode-sdk/src/ensapi/api/name-tokens/examples.ts create mode 100644 packages/ensnode-sdk/src/ensapi/api/registrar-actions/examples.ts create mode 100644 packages/ensnode-sdk/src/ensapi/api/resolution/examples.ts create mode 100644 packages/ensnode-sdk/src/ensapi/api/resolution/zod-schemas.test.ts diff --git a/apps/ensapi/src/app.ts b/apps/ensapi/src/app.ts index 1a6b2121b9..e2b4997c56 100644 --- a/apps/ensapi/src/app.ts +++ b/apps/ensapi/src/app.ts @@ -71,7 +71,7 @@ app.get("/health", async (c) => { // log hono errors to console app.onError((error, ctx) => { logger.error(error); - return errorResponse(ctx, "Internal Server Error"); + return errorResponse(ctx, error); }); export default app; diff --git a/apps/ensapi/src/handlers/api/explore/name-tokens-api.routes.ts b/apps/ensapi/src/handlers/api/explore/name-tokens-api.routes.ts index 01a1c773f4..291074a30d 100644 --- a/apps/ensapi/src/handlers/api/explore/name-tokens-api.routes.ts +++ b/apps/ensapi/src/handlers/api/explore/name-tokens-api.routes.ts @@ -4,6 +4,7 @@ import { ErrorResponseSchema, makeNameTokensResponseSchema, makeNodeSchema, + nameTokensResponseOkExample, } from "@ensnode/ensnode-sdk/internal"; import { params } from "@/lib/handlers/params.schema"; @@ -42,7 +43,9 @@ export const getNameTokensRoute = createRoute({ description: "Name tokens known", content: { "application/json": { - schema: makeNameTokensResponseSchema("Name Tokens Response", true), + schema: makeNameTokensResponseSchema("Name Tokens Response", true).openapi({ + example: nameTokensResponseOkExample, + }), }, }, }, @@ -81,5 +84,3 @@ export const getNameTokensRoute = createRoute({ }, }, }); - -export const routes = [getNameTokensRoute]; diff --git a/apps/ensapi/src/handlers/api/explore/registrar-actions-api.routes.ts b/apps/ensapi/src/handlers/api/explore/registrar-actions-api.routes.ts index 6c42f2823f..8415002800 100644 --- a/apps/ensapi/src/handlers/api/explore/registrar-actions-api.routes.ts +++ b/apps/ensapi/src/handlers/api/explore/registrar-actions-api.routes.ts @@ -6,10 +6,13 @@ import { RegistrarActionsOrders, } from "@ensnode/ensnode-sdk"; import { + ErrorResponseSchema, + makeLowercaseAddressSchema, makeNodeSchema, - makeNormalizedAddressSchema, makePositiveIntegerSchema, + makeRegistrarActionsResponseOkSchema, makeUnixTimestampSchema, + registrarActionsResponseOkExample, } from "@ensnode/ensnode-sdk/internal"; import { params } from "@/lib/handlers/params.schema"; @@ -51,7 +54,7 @@ export const registrarActionsQuerySchema = z .describe("Filter to only include actions with referrals") .openapi({ default: false }), - decodedReferrer: makeNormalizedAddressSchema("decodedReferrer") + decodedReferrer: makeLowercaseAddressSchema("decodedReferrer") .optional() .describe("Filter by decoded referrer address"), @@ -59,12 +62,14 @@ export const registrarActionsQuerySchema = z .pipe(z.coerce.number()) .pipe(makeUnixTimestampSchema("beginTimestamp")) .optional() + .openapi({ type: "integer" }) .describe("Filter actions at or after this Unix timestamp"), endTimestamp: params.queryParam .pipe(z.coerce.number()) .pipe(makeUnixTimestampSchema("endTimestamp")) .optional() + .openapi({ type: "integer" }) .describe("Filter actions at or before this Unix timestamp"), }) .refine( @@ -96,12 +101,27 @@ export const getRegistrarActionsRoute = createRoute({ responses: { 200: { description: "Successfully retrieved registrar actions", + content: { + "application/json": { + schema: makeRegistrarActionsResponseOkSchema().openapi({ + example: registrarActionsResponseOkExample, + }), + }, + }, }, 400: { description: "Invalid query", + content: { "application/json": { schema: ErrorResponseSchema } }, }, 500: { description: "Internal server error", + // TODO: fix the problem with typechecking. + // Throws error ```Type '{ currency: "ETH"; amount: string; }' is not assignable to type 'null'.``` + // content: { + // "application/json": { + // schema: makeRegistrarActionsResponseErrorSchema("Registrar Actions Error Response"), + // }, + // }, }, }, }); @@ -125,14 +145,31 @@ export const getRegistrarActionsByParentNodeRoute = createRoute({ responses: { 200: { description: "Successfully retrieved registrar actions", + content: { + "application/json": { + schema: makeRegistrarActionsResponseOkSchema( + "Registrar Actions By ParentNode Response", + ).openapi({ + example: registrarActionsResponseOkExample, + }), + }, + }, }, 400: { description: "Invalid input", + content: { "application/json": { schema: ErrorResponseSchema } }, }, 500: { description: "Internal server error", + // TODO: fix the problem with typechecking. + // Throws error ```Type '{ currency: "ETH"; amount: string; }' is not assignable to type 'null'.``` + // content: { + // "application/json": { + // schema: makeRegistrarActionsResponseErrorSchema( + // "Registrar Actions By ParentNode Error Response", + // ), + // }, + // }, }, }, }); - -export const routes = [getRegistrarActionsRoute, getRegistrarActionsByParentNodeRoute]; diff --git a/apps/ensapi/src/handlers/api/explore/registrar-actions-api.ts b/apps/ensapi/src/handlers/api/explore/registrar-actions-api.ts index 5b0a48892b..89cd1414fa 100644 --- a/apps/ensapi/src/handlers/api/explore/registrar-actions-api.ts +++ b/apps/ensapi/src/handlers/api/explore/registrar-actions-api.ts @@ -122,6 +122,7 @@ app.openapi(getRegistrarActionsRoute, async (c) => { pageContext, accurateAsOf, } satisfies RegistrarActionsResponseOk), + 200, ); } catch (error) { const errorMessage = error instanceof Error ? error.message : "Unknown error"; @@ -196,6 +197,7 @@ app.openapi(getRegistrarActionsByParentNodeRoute, async (c) => { pageContext, accurateAsOf, } satisfies RegistrarActionsResponseOk), + 200, ); } catch (error) { const errorMessage = error instanceof Error ? error.message : "Unknown error"; diff --git a/apps/ensapi/src/handlers/api/meta/realtime-api.routes.ts b/apps/ensapi/src/handlers/api/meta/realtime-api.routes.ts index 91fc7f3cff..601752ee3c 100644 --- a/apps/ensapi/src/handlers/api/meta/realtime-api.routes.ts +++ b/apps/ensapi/src/handlers/api/meta/realtime-api.routes.ts @@ -46,5 +46,3 @@ export const realtimeGetMeta = createRoute({ }, }, }); - -export const routes = [realtimeGetMeta]; diff --git a/apps/ensapi/src/handlers/api/meta/status-api.routes.ts b/apps/ensapi/src/handlers/api/meta/status-api.routes.ts index a8487d3856..2fbd41f22d 100644 --- a/apps/ensapi/src/handlers/api/meta/status-api.routes.ts +++ b/apps/ensapi/src/handlers/api/meta/status-api.routes.ts @@ -53,5 +53,3 @@ export const getIndexingStatusRoute = createRoute({ }, }, }); - -export const routes = [getConfigRoute, getIndexingStatusRoute]; diff --git a/apps/ensapi/src/handlers/api/resolution/resolution-api.routes.ts b/apps/ensapi/src/handlers/api/resolution/resolution-api.routes.ts index d22fd91d77..1869f02108 100644 --- a/apps/ensapi/src/handlers/api/resolution/resolution-api.routes.ts +++ b/apps/ensapi/src/handlers/api/resolution/resolution-api.routes.ts @@ -4,6 +4,9 @@ import { makeResolvePrimaryNameResponseSchema, makeResolvePrimaryNamesResponseSchema, makeResolveRecordsResponseSchema, + resolvePrimaryNameResponseExample, + resolvePrimaryNamesResponseExample, + resolveRecordsResponseExample, } from "@ensnode/ensnode-sdk/internal"; import { params } from "@/lib/handlers/params.schema"; @@ -28,7 +31,9 @@ export const resolveRecordsRoute = createRoute({ description: "Successfully resolved records", content: { "application/json": { - schema: makeResolveRecordsResponseSchema(), + schema: makeResolveRecordsResponseSchema().openapi({ + example: resolveRecordsResponseExample, + }), }, }, }, @@ -57,7 +62,9 @@ export const resolvePrimaryNameRoute = createRoute({ description: "Successfully resolved name", content: { "application/json": { - schema: makeResolvePrimaryNameResponseSchema(), + schema: makeResolvePrimaryNameResponseSchema().openapi({ + example: resolvePrimaryNameResponseExample, + }), }, }, }, @@ -86,11 +93,11 @@ export const resolvePrimaryNamesRoute = createRoute({ description: "Successfully resolved records", content: { "application/json": { - schema: makeResolvePrimaryNamesResponseSchema(), + schema: makeResolvePrimaryNamesResponseSchema().openapi({ + example: resolvePrimaryNamesResponseExample, + }), }, }, }, }, }); - -export const routes = [resolveRecordsRoute, resolvePrimaryNameRoute, resolvePrimaryNamesRoute]; diff --git a/apps/ensapi/src/lib/handlers/params.schema.ts b/apps/ensapi/src/lib/handlers/params.schema.ts index 489ad728f4..b3b01b2052 100644 --- a/apps/ensapi/src/lib/handlers/params.schema.ts +++ b/apps/ensapi/src/lib/handlers/params.schema.ts @@ -5,7 +5,7 @@ import { isSelectionEmpty, type ResolverRecordsSelection } from "@ensnode/ensnod import { makeCoinTypeStringSchema, makeDefaultableChainIdStringSchema, - makeNormalizedAddressSchema, + makeLowercaseAddressSchema, } from "@ensnode/ensnode-sdk/internal"; const excludingDefaultChainId = z @@ -32,58 +32,82 @@ const stringarray = z const name = z .string() .refine(isNormalizedName, "Must be normalized, see https://docs.ens.domains/resolution/names/") - .transform((val) => val as Name); - -const trace = z.optional(boolstring).default(false).openapi({ default: false }); -const accelerate = z.optional(boolstring).default(false).openapi({ default: false }); -const address = makeNormalizedAddressSchema(); -const defaultableChainId = makeDefaultableChainIdStringSchema(); + .transform((val) => val as Name) + .describe("ENS name to resolve (e.g. 'vitalik.eth'). Must be normalized per ENSIP-15."); + +const trace = z + .optional(boolstring) + .default(false) + .describe("Include detailed resolution trace information in the response.") + .openapi({ default: false }); + +const accelerate = z + .optional(boolstring) + .default(false) + .describe("Attempt accelerated CCIP-Read resolution using L1 data.") + .openapi({ + default: false, + }); +const address = makeLowercaseAddressSchema().describe( + "EVM wallet address (e.g. '0xd8da6bf26964af9d7eed9e03e53415d37aa96045').", +); +const defaultableChainId = makeDefaultableChainIdStringSchema().describe( + "Chain ID as a string (e.g. '1' for Ethereum mainnet). Use '0' for the default EVM chain.", +); const coinType = makeCoinTypeStringSchema(); -const chainIdsWithoutDefaultChainId = z.optional( - stringarray.pipe(z.array(defaultableChainId.pipe(excludingDefaultChainId))), -); +const chainIdsWithoutDefaultChainId = z + .optional(stringarray.pipe(z.array(defaultableChainId.pipe(excludingDefaultChainId)))) + .describe( + "Comma-separated list of chain IDs to resolve primary names for (e.g. '1,10,8453'). The default EVM chain ID (0) is not allowed.", + ); const rawSelectionParams = z.object({ - name: z.string().optional(), - addresses: z.string().optional(), - texts: z.string().optional(), + nameRecord: z + .string() + .optional() + .describe("Whether to include the ENS name record in the response.") + .openapi({ + enum: ["true", "false"], + }), + addresses: z + .string() + .optional() + .describe( + "Comma-separated list of coin types to resolve addresses for (e.g. '60' for ETH, '2147483658' for OP).", + ), + texts: z + .string() + .optional() + .describe( + "Comma-separated list of text record keys to resolve (e.g. 'avatar,description,url').", + ), }); -const selectionFields = z.object({ - name: z.optional(boolstring), - addresses: z.optional(stringarray.pipe(z.array(coinType))), - texts: z.optional(stringarray), -}); - -type SelectionFields = z.output; - -function toSelection( - fields: SelectionFields, - ctx: z.RefinementCtx, -): ResolverRecordsSelection | typeof z.NEVER { - const sel: ResolverRecordsSelection = { - ...(fields.name && { name: true }), - ...(fields.addresses && { addresses: fields.addresses }), - ...(fields.texts && { texts: fields.texts }), - }; - - if (isSelectionEmpty(sel)) { - ctx.issues.push({ code: "custom", message: "Selection cannot be empty.", input: sel }); - return z.NEVER; - } - - return sel; -} - -const selection = selectionFields.transform(toSelection); - -const resolveRecordsQuery = z - .object({ ...selectionFields.shape, trace, accelerate }) - .transform(({ trace, accelerate, ...fields }, ctx) => { - const sel = toSelection(fields, ctx); - if (sel === z.NEVER) return z.NEVER; - return { selection: sel, trace, accelerate }; +const selection = z + .object({ + nameRecord: z.optional(boolstring), + addresses: z.optional(stringarray.pipe(z.array(coinType))), + texts: z.optional(stringarray), + }) + .transform((value, ctx) => { + const selection: ResolverRecordsSelection = { + ...(value.nameRecord && { name: true }), + ...(value.addresses && { addresses: value.addresses }), + ...(value.texts && { texts: value.texts }), + }; + + if (isSelectionEmpty(selection)) { + ctx.issues.push({ + code: "custom", + message: "Selection cannot be empty.", + input: selection, + }); + + return z.NEVER; + } + + return selection; }); /** @@ -117,7 +141,6 @@ export const params = { coinType, selectionParams: rawSelectionParams, selection, - resolveRecordsQuery, chainIdsWithoutDefaultChainId, queryParam, }; diff --git a/docs/docs.ensnode.io/ensapi-openapi.json b/docs/docs.ensnode.io/ensapi-openapi.json index e4c4dbd20e..11ae8efabe 100644 --- a/docs/docs.ensnode.io/ensapi-openapi.json +++ b/docs/docs.ensnode.io/ensapi-openapi.json @@ -969,19 +969,66 @@ "summary": "Resolve ENS Records", "description": "Resolves ENS records for a given name", "parameters": [ - { "schema": { "type": "string" }, "required": true, "name": "name", "in": "path" }, - { "schema": { "type": "boolean" }, "required": false, "name": "name", "in": "query" }, - { "schema": { "type": "string" }, "required": false, "name": "addresses", "in": "query" }, - { "schema": { "type": "string" }, "required": false, "name": "texts", "in": "query" }, { - "schema": { "type": "boolean", "default": false }, + "schema": { + "type": "string", + "description": "ENS name to resolve (e.g. 'vitalik.eth'). Must be normalized per ENSIP-15." + }, + "required": true, + "description": "ENS name to resolve (e.g. 'vitalik.eth'). Must be normalized per ENSIP-15.", + "name": "name", + "in": "path" + }, + { + "schema": { + "type": "string", + "description": "Whether to include the ENS name record in the response.", + "enum": ["true", "false"] + }, + "required": false, + "description": "Whether to include the ENS name record in the response.", + "name": "nameRecord", + "in": "query" + }, + { + "schema": { + "type": "string", + "description": "Comma-separated list of coin types to resolve addresses for (e.g. '60' for ETH, '2147483658' for OP)." + }, + "required": false, + "description": "Comma-separated list of coin types to resolve addresses for (e.g. '60' for ETH, '2147483658' for OP).", + "name": "addresses", + "in": "query" + }, + { + "schema": { + "type": "string", + "description": "Comma-separated list of text record keys to resolve (e.g. 'avatar,description,url')." + }, + "required": false, + "description": "Comma-separated list of text record keys to resolve (e.g. 'avatar,description,url').", + "name": "texts", + "in": "query" + }, + { + "schema": { + "type": "boolean", + "description": "Include detailed resolution trace information in the response.", + "default": false + }, "required": false, + "description": "Include detailed resolution trace information in the response.", "name": "trace", "in": "query" }, { - "schema": { "type": "boolean", "default": false }, + "schema": { + "type": "boolean", + "description": "Attempt accelerated CCIP-Read resolution using L1 data.", + "default": false + }, "required": false, + "description": "Attempt accelerated CCIP-Read resolution using L1 data.", "name": "accelerate", "in": "query" } @@ -1012,7 +1059,16 @@ "accelerationAttempted": { "type": "boolean" }, "trace": { "type": "array", "items": {} } }, - "required": ["records", "accelerationRequested", "accelerationAttempted"] + "required": ["records", "accelerationRequested", "accelerationAttempted"], + "example": { + "records": { + "name": "vitalik.eth", + "addresses": { "60": "0xd8da6bf26964af9d7eed9e03e53415d37aa96045" }, + "texts": { "description": "mi pinxe lo crino tcati" } + }, + "accelerationRequested": false, + "accelerationAttempted": false + } } } } @@ -1027,17 +1083,45 @@ "summary": "Resolve Primary Name", "description": "Resolves a primary name for a given `address` and `chainId`", "parameters": [ - { "schema": { "type": "string" }, "required": true, "name": "address", "in": "path" }, - { "schema": { "type": "string" }, "required": true, "name": "chainId", "in": "path" }, { - "schema": { "type": "boolean", "default": false }, + "schema": { + "type": "string", + "description": "EVM wallet address (e.g. '0xd8da6bf26964af9d7eed9e03e53415d37aa96045')." + }, + "required": true, + "description": "EVM wallet address (e.g. '0xd8da6bf26964af9d7eed9e03e53415d37aa96045').", + "name": "address", + "in": "path" + }, + { + "schema": { + "type": "string", + "description": "Chain ID as a string (e.g. '1' for Ethereum mainnet). Use '0' for the default EVM chain." + }, + "required": true, + "description": "Chain ID as a string (e.g. '1' for Ethereum mainnet). Use '0' for the default EVM chain.", + "name": "chainId", + "in": "path" + }, + { + "schema": { + "type": "boolean", + "description": "Include detailed resolution trace information in the response.", + "default": false + }, "required": false, + "description": "Include detailed resolution trace information in the response.", "name": "trace", "in": "query" }, { - "schema": { "type": "boolean", "default": false }, + "schema": { + "type": "boolean", + "description": "Attempt accelerated CCIP-Read resolution using L1 data.", + "default": false + }, "required": false, + "description": "Attempt accelerated CCIP-Read resolution using L1 data.", "name": "accelerate", "in": "query" } @@ -1055,7 +1139,12 @@ "accelerationAttempted": { "type": "boolean" }, "trace": { "type": "array", "items": {} } }, - "required": ["name", "accelerationRequested", "accelerationAttempted"] + "required": ["name", "accelerationRequested", "accelerationAttempted"], + "example": { + "name": "jesse.base.eth", + "accelerationRequested": false, + "accelerationAttempted": false + } } } } @@ -1070,17 +1159,45 @@ "summary": "Resolve Primary Names", "description": "Resolves all primary names for a given address across multiple chains", "parameters": [ - { "schema": { "type": "string" }, "required": true, "name": "address", "in": "path" }, - { "schema": { "type": "string" }, "required": false, "name": "chainIds", "in": "query" }, { - "schema": { "type": "boolean", "default": false }, + "schema": { + "type": "string", + "description": "EVM wallet address (e.g. '0xd8da6bf26964af9d7eed9e03e53415d37aa96045')." + }, + "required": true, + "description": "EVM wallet address (e.g. '0xd8da6bf26964af9d7eed9e03e53415d37aa96045').", + "name": "address", + "in": "path" + }, + { + "schema": { + "type": "string", + "description": "Comma-separated list of chain IDs to resolve primary names for (e.g. '1,10,8453'). The default EVM chain ID (0) is not allowed." + }, + "required": false, + "description": "Comma-separated list of chain IDs to resolve primary names for (e.g. '1,10,8453'). The default EVM chain ID (0) is not allowed.", + "name": "chainIds", + "in": "query" + }, + { + "schema": { + "type": "boolean", + "description": "Include detailed resolution trace information in the response.", + "default": false + }, "required": false, + "description": "Include detailed resolution trace information in the response.", "name": "trace", "in": "query" }, { - "schema": { "type": "boolean", "default": false }, + "schema": { + "type": "boolean", + "description": "Attempt accelerated CCIP-Read resolution using L1 data.", + "default": false + }, "required": false, + "description": "Attempt accelerated CCIP-Read resolution using L1 data.", "name": "accelerate", "in": "query" } @@ -1101,7 +1218,19 @@ "accelerationAttempted": { "type": "boolean" }, "trace": { "type": "array", "items": {} } }, - "required": ["names", "accelerationRequested", "accelerationAttempted"] + "required": ["names", "accelerationRequested", "accelerationAttempted"], + "example": { + "names": { + "1": "jesse.base.eth", + "10": null, + "8453": "jesse.base.eth", + "42161": null, + "59144": null, + "534352": null + }, + "accelerationRequested": false, + "accelerationAttempted": false + } } } } @@ -1328,7 +1457,36 @@ } ] } - ] + ], + "example": { + "responseCode": "ok", + "registeredNameTokens": { + "domainId": "0xee6c4522aab0003e8d14cd40a6af439055fd2577951148c14b6cea9a53475835", + "name": "vitalik.eth", + "tokens": [ + { + "token": { + "assetNamespace": "erc721", + "contract": { + "chainId": 1, + "address": "0x57f1887a8bf19b14fc0df6fd9b2acc9af147ea85" + }, + "tokenId": "0xaf2caa1c2ca1d027f1ac823b529d0a67cd144264b2789fa2ea4d63a67c7103cc" + }, + "ownership": { + "ownershipType": "fully-onchain", + "owner": { + "chainId": 1, + "address": "0x220866b1a2219f40e72f5c628b65d54268ca3a9d" + } + }, + "mintStatus": "minted" + } + ], + "expiresAt": 2461152330, + "accurateAsOf": 1700000000 + } + } } } } @@ -1824,173 +1982,1039 @@ "name": "decodedReferrer", "in": "query" }, - { - "schema": { "description": "Filter actions at or after this Unix timestamp" }, - "required": false, - "description": "Filter actions at or after this Unix timestamp", - "name": "beginTimestamp", - "in": "query" - }, - { - "schema": { "description": "Filter actions at or before this Unix timestamp" }, - "required": false, - "description": "Filter actions at or before this Unix timestamp", - "name": "endTimestamp", - "in": "query" - } - ], - "responses": { - "200": { "description": "Successfully retrieved registrar actions" }, - "400": { "description": "Invalid query" }, - "500": { "description": "Internal server error" } - } - } - }, - "/api/registrar-actions/{parentNode}": { - "get": { - "operationId": "getRegistrarActionsByParentNode", - "tags": ["Explore"], - "summary": "Get Registrar Actions by Parent Node", - "description": "Returns registrar actions filtered by parent node hash with optional additional filtering and pagination", - "parameters": [ - { - "schema": { - "type": "string", - "description": "Parent node to filter registrar actions" - }, - "required": true, - "description": "Parent node to filter registrar actions", - "name": "parentNode", - "in": "path" - }, - { - "schema": { - "type": "string", - "enum": ["orderBy[timestamp]=desc"], - "default": "orderBy[timestamp]=desc", - "description": "Order of results" - }, - "required": false, - "description": "Order of results", - "name": "orderBy", - "in": "query" - }, - { - "schema": { - "type": "integer", - "minimum": 1, - "default": 1, - "description": "Page number for pagination" - }, - "required": false, - "description": "Page number for pagination", - "name": "page", - "in": "query" - }, { "schema": { "type": "integer", - "minimum": 1, - "maximum": 100, - "default": 10, - "description": "Number of records per page" - }, - "required": false, - "description": "Number of records per page", - "name": "recordsPerPage", - "in": "query" - }, - { - "schema": { - "type": "boolean", - "description": "Filter to only include actions with referrals", - "default": false + "description": "Filter actions at or after this Unix timestamp" }, "required": false, - "description": "Filter to only include actions with referrals", - "name": "withReferral", - "in": "query" - }, - { - "schema": { "type": "string", "description": "Filter by decoded referrer address" }, - "required": false, - "description": "Filter by decoded referrer address", - "name": "decodedReferrer", - "in": "query" - }, - { - "schema": { "description": "Filter actions at or after this Unix timestamp" }, - "required": false, "description": "Filter actions at or after this Unix timestamp", "name": "beginTimestamp", "in": "query" }, - { - "schema": { "description": "Filter actions at or before this Unix timestamp" }, - "required": false, - "description": "Filter actions at or before this Unix timestamp", - "name": "endTimestamp", - "in": "query" - } - ], - "responses": { - "200": { "description": "Successfully retrieved registrar actions" }, - "400": { "description": "Invalid input" }, - "500": { "description": "Internal server error" } - } - } - }, - "/v1/ensanalytics/referral-leaderboard": { - "get": { - "operationId": "getReferralLeaderboard", - "tags": ["ENSAwards"], - "summary": "Get Referrer Leaderboard", - "description": "Returns a paginated page from the referrer leaderboard for a specific edition", - "parameters": [ - { - "schema": { "type": "string", "minLength": 1, "pattern": "^[a-z0-9]+(-[a-z0-9]+)*$" }, - "required": true, - "name": "edition", - "in": "query" - }, - { - "schema": { - "type": "integer", - "minimum": 1, - "description": "Page number for pagination" - }, - "required": false, - "description": "Page number for pagination", - "name": "page", - "in": "query" - }, { "schema": { "type": "integer", - "minimum": 1, - "maximum": 100, - "description": "Number of referrers per page" + "description": "Filter actions at or before this Unix timestamp" }, "required": false, - "description": "Number of referrers per page", - "name": "recordsPerPage", + "description": "Filter actions at or before this Unix timestamp", + "name": "endTimestamp", "in": "query" } ], "responses": { - "200": { "description": "Successfully retrieved referrer leaderboard page" }, - "400": { "description": "Invalid request" }, - "404": { "description": "Unknown edition slug" }, - "500": { "description": "Internal server error" }, - "503": { "description": "Service unavailable" } - } - } - }, - "/v1/ensanalytics/referrer/{referrer}": { - "get": { - "operationId": "getReferrerDetail", + "200": { + "description": "Successfully retrieved registrar actions", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "responseCode": { "type": "string", "enum": ["ok"] }, + "registrarActions": { + "type": "array", + "items": { + "type": "object", + "properties": { + "action": { + "oneOf": [ + { + "type": "object", + "properties": { + "id": { "type": "string", "minLength": 1 }, + "incrementalDuration": { "type": "number" }, + "registrant": { "type": "string" }, + "registrationLifecycle": { + "type": "object", + "properties": { + "subregistry": { + "type": "object", + "properties": { + "subregistryId": { + "type": "object", + "properties": { + "chainId": { + "type": "integer", + "exclusiveMinimum": 0 + }, + "address": { "type": "string" } + }, + "required": ["chainId", "address"], + "additionalProperties": false + }, + "node": { "type": "string" } + }, + "required": ["subregistryId", "node"] + }, + "node": { "type": "string" }, + "expiresAt": { "type": "integer" } + }, + "required": ["subregistry", "node", "expiresAt"] + }, + "pricing": { + "anyOf": [ + { + "type": "object", + "properties": { + "baseCost": { + "type": "object", + "properties": { + "amount": { "type": "string", "pattern": "^d+$" }, + "currency": { "type": "string", "enum": ["ETH"] } + }, + "required": ["amount", "currency"], + "additionalProperties": false + }, + "premium": { + "type": "object", + "properties": { + "amount": { "type": "string", "pattern": "^d+$" }, + "currency": { "type": "string", "enum": ["ETH"] } + }, + "required": ["amount", "currency"], + "additionalProperties": false + }, + "total": { + "type": "object", + "properties": { + "amount": { "type": "string", "pattern": "^d+$" }, + "currency": { "type": "string", "enum": ["ETH"] } + }, + "required": ["amount", "currency"], + "additionalProperties": false + } + }, + "required": ["baseCost", "premium", "total"] + }, + { + "type": "object", + "properties": { + "baseCost": { "type": "null" }, + "premium": { "type": "null" }, + "total": { "type": "null" } + }, + "required": ["baseCost", "premium", "total"] + } + ] + }, + "referral": { + "anyOf": [ + { + "type": "object", + "properties": { + "encodedReferrer": { "type": "string" }, + "decodedReferrer": { "type": "string" } + }, + "required": ["encodedReferrer", "decodedReferrer"] + }, + { + "type": "object", + "properties": { + "encodedReferrer": { "type": "null" }, + "decodedReferrer": { "type": "null" } + }, + "required": ["encodedReferrer", "decodedReferrer"] + } + ] + }, + "block": { + "type": "object", + "properties": { + "timestamp": { "type": "integer" }, + "number": { "type": "integer", "minimum": 0 } + }, + "required": ["timestamp", "number"], + "additionalProperties": false + }, + "transactionHash": { "type": "string" }, + "eventIds": { + "type": "array", + "items": { "type": "string", "minLength": 1 }, + "minItems": 1 + }, + "type": { "type": "string", "enum": ["registration"] } + }, + "required": [ + "id", + "incrementalDuration", + "registrant", + "registrationLifecycle", + "pricing", + "referral", + "block", + "transactionHash", + "eventIds", + "type" + ] + }, + { + "type": "object", + "properties": { + "id": { "type": "string", "minLength": 1 }, + "incrementalDuration": { "type": "number" }, + "registrant": { "type": "string" }, + "registrationLifecycle": { + "type": "object", + "properties": { + "subregistry": { + "type": "object", + "properties": { + "subregistryId": { + "type": "object", + "properties": { + "chainId": { + "type": "integer", + "exclusiveMinimum": 0 + }, + "address": { "type": "string" } + }, + "required": ["chainId", "address"], + "additionalProperties": false + }, + "node": { "type": "string" } + }, + "required": ["subregistryId", "node"] + }, + "node": { "type": "string" }, + "expiresAt": { "type": "integer" } + }, + "required": ["subregistry", "node", "expiresAt"] + }, + "pricing": { + "anyOf": [ + { + "type": "object", + "properties": { + "baseCost": { + "type": "object", + "properties": { + "amount": { "type": "string", "pattern": "^d+$" }, + "currency": { "type": "string", "enum": ["ETH"] } + }, + "required": ["amount", "currency"], + "additionalProperties": false + }, + "premium": { + "type": "object", + "properties": { + "amount": { "type": "string", "pattern": "^d+$" }, + "currency": { "type": "string", "enum": ["ETH"] } + }, + "required": ["amount", "currency"], + "additionalProperties": false + }, + "total": { + "type": "object", + "properties": { + "amount": { "type": "string", "pattern": "^d+$" }, + "currency": { "type": "string", "enum": ["ETH"] } + }, + "required": ["amount", "currency"], + "additionalProperties": false + } + }, + "required": ["baseCost", "premium", "total"] + }, + { + "type": "object", + "properties": { + "baseCost": { "type": "null" }, + "premium": { "type": "null" }, + "total": { "type": "null" } + }, + "required": ["baseCost", "premium", "total"] + } + ] + }, + "referral": { + "anyOf": [ + { + "type": "object", + "properties": { + "encodedReferrer": { "type": "string" }, + "decodedReferrer": { "type": "string" } + }, + "required": ["encodedReferrer", "decodedReferrer"] + }, + { + "type": "object", + "properties": { + "encodedReferrer": { "type": "null" }, + "decodedReferrer": { "type": "null" } + }, + "required": ["encodedReferrer", "decodedReferrer"] + } + ] + }, + "block": { + "type": "object", + "properties": { + "timestamp": { "type": "integer" }, + "number": { "type": "integer", "minimum": 0 } + }, + "required": ["timestamp", "number"], + "additionalProperties": false + }, + "transactionHash": { "type": "string" }, + "eventIds": { + "type": "array", + "items": { "type": "string", "minLength": 1 }, + "minItems": 1 + }, + "type": { "type": "string", "enum": ["renewal"] } + }, + "required": [ + "id", + "incrementalDuration", + "registrant", + "registrationLifecycle", + "pricing", + "referral", + "block", + "transactionHash", + "eventIds", + "type" + ] + } + ] + }, + "name": { "type": "string" } + }, + "required": ["action", "name"] + } + }, + "pageContext": { + "anyOf": [ + { + "type": "object", + "properties": { + "totalRecords": { "type": "number", "enum": [0] }, + "totalPages": { "type": "number", "enum": [1] }, + "hasNext": { "type": "boolean", "enum": [false] }, + "hasPrev": { "type": "boolean", "enum": [false] }, + "page": { "type": "integer", "exclusiveMinimum": 0 }, + "recordsPerPage": { + "type": "integer", + "exclusiveMinimum": 0, + "maximum": 100 + } + }, + "required": [ + "totalRecords", + "totalPages", + "hasNext", + "hasPrev", + "page", + "recordsPerPage" + ] + }, + { + "type": "object", + "properties": { + "totalRecords": { "type": "integer", "exclusiveMinimum": 0 }, + "totalPages": { "type": "integer", "exclusiveMinimum": 0 }, + "hasNext": { "type": "boolean" }, + "hasPrev": { "type": "boolean" }, + "startIndex": { "type": "integer", "minimum": 0 }, + "endIndex": { "type": "integer", "minimum": 0 }, + "page": { "type": "integer", "exclusiveMinimum": 0 }, + "recordsPerPage": { + "type": "integer", + "exclusiveMinimum": 0, + "maximum": 100 + } + }, + "required": [ + "totalRecords", + "totalPages", + "hasNext", + "hasPrev", + "startIndex", + "endIndex", + "page", + "recordsPerPage" + ] + } + ] + }, + "accurateAsOf": { "type": "integer" } + }, + "required": ["responseCode", "registrarActions", "pageContext"], + "example": { + "responseCode": "ok", + "registrarActions": [ + { + "action": { + "type": "registration", + "id": "0x0000000000000000000000000000000000000000000000000000000000000001", + "incrementalDuration": 31536000, + "registrant": "0xd8da6bf26964af9d7eed9e03e53415d37aa96045", + "registrationLifecycle": { + "subregistry": { + "subregistryId": { + "chainId": 1, + "address": "0x253553366da8546fc250f225fe3d25d0c782303b" + }, + "node": "0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae" + }, + "node": "0xee6c4522aab0003e8d14cd40a6af439055fd2577951148c14b6cea9a53475835", + "expiresAt": 1893456000 + }, + "pricing": { + "baseCost": { "amount": "1000000000000000", "currency": "ETH" }, + "premium": { "amount": "0", "currency": "ETH" }, + "total": { "amount": "1000000000000000", "currency": "ETH" } + }, + "referral": { "encodedReferrer": null, "decodedReferrer": null }, + "block": { "timestamp": 1700000000, "number": 18500000 }, + "transactionHash": "0x0000000000000000000000000000000000000000000000000000000000000001", + "eventIds": [ + "0x0000000000000000000000000000000000000000000000000000000000000001" + ] + }, + "name": "vitalik.eth" + } + ], + "pageContext": { + "page": 1, + "recordsPerPage": 25, + "totalRecords": 1, + "totalPages": 1, + "hasNext": false, + "hasPrev": false, + "startIndex": 0, + "endIndex": 0 + }, + "accurateAsOf": 1700000000 + } + } + } + } + }, + "400": { + "description": "Invalid query", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { "message": { "type": "string" }, "details": {} }, + "required": ["message"] + } + } + } + }, + "500": { "description": "Internal server error" } + } + } + }, + "/api/registrar-actions/{parentNode}": { + "get": { + "operationId": "getRegistrarActionsByParentNode", + "tags": ["Explore"], + "summary": "Get Registrar Actions by Parent Node", + "description": "Returns registrar actions filtered by parent node hash with optional additional filtering and pagination", + "parameters": [ + { + "schema": { + "type": "string", + "description": "Parent node to filter registrar actions" + }, + "required": true, + "description": "Parent node to filter registrar actions", + "name": "parentNode", + "in": "path" + }, + { + "schema": { + "type": "string", + "enum": ["orderBy[timestamp]=desc"], + "default": "orderBy[timestamp]=desc", + "description": "Order of results" + }, + "required": false, + "description": "Order of results", + "name": "orderBy", + "in": "query" + }, + { + "schema": { + "type": "integer", + "minimum": 1, + "default": 1, + "description": "Page number for pagination" + }, + "required": false, + "description": "Page number for pagination", + "name": "page", + "in": "query" + }, + { + "schema": { + "type": "integer", + "minimum": 1, + "maximum": 100, + "default": 10, + "description": "Number of records per page" + }, + "required": false, + "description": "Number of records per page", + "name": "recordsPerPage", + "in": "query" + }, + { + "schema": { + "type": "boolean", + "description": "Filter to only include actions with referrals", + "default": false + }, + "required": false, + "description": "Filter to only include actions with referrals", + "name": "withReferral", + "in": "query" + }, + { + "schema": { "type": "string", "description": "Filter by decoded referrer address" }, + "required": false, + "description": "Filter by decoded referrer address", + "name": "decodedReferrer", + "in": "query" + }, + { + "schema": { + "type": "integer", + "description": "Filter actions at or after this Unix timestamp" + }, + "required": false, + "description": "Filter actions at or after this Unix timestamp", + "name": "beginTimestamp", + "in": "query" + }, + { + "schema": { + "type": "integer", + "description": "Filter actions at or before this Unix timestamp" + }, + "required": false, + "description": "Filter actions at or before this Unix timestamp", + "name": "endTimestamp", + "in": "query" + } + ], + "responses": { + "200": { + "description": "Successfully retrieved registrar actions", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "responseCode": { "type": "string", "enum": ["ok"] }, + "registrarActions": { + "type": "array", + "items": { + "type": "object", + "properties": { + "action": { + "oneOf": [ + { + "type": "object", + "properties": { + "id": { "type": "string", "minLength": 1 }, + "incrementalDuration": { "type": "number" }, + "registrant": { "type": "string" }, + "registrationLifecycle": { + "type": "object", + "properties": { + "subregistry": { + "type": "object", + "properties": { + "subregistryId": { + "type": "object", + "properties": { + "chainId": { + "type": "integer", + "exclusiveMinimum": 0 + }, + "address": { "type": "string" } + }, + "required": ["chainId", "address"], + "additionalProperties": false + }, + "node": { "type": "string" } + }, + "required": ["subregistryId", "node"] + }, + "node": { "type": "string" }, + "expiresAt": { "type": "integer" } + }, + "required": ["subregistry", "node", "expiresAt"] + }, + "pricing": { + "anyOf": [ + { + "type": "object", + "properties": { + "baseCost": { + "type": "object", + "properties": { + "amount": { "type": "string", "pattern": "^d+$" }, + "currency": { "type": "string", "enum": ["ETH"] } + }, + "required": ["amount", "currency"], + "additionalProperties": false + }, + "premium": { + "type": "object", + "properties": { + "amount": { "type": "string", "pattern": "^d+$" }, + "currency": { "type": "string", "enum": ["ETH"] } + }, + "required": ["amount", "currency"], + "additionalProperties": false + }, + "total": { + "type": "object", + "properties": { + "amount": { "type": "string", "pattern": "^d+$" }, + "currency": { "type": "string", "enum": ["ETH"] } + }, + "required": ["amount", "currency"], + "additionalProperties": false + } + }, + "required": ["baseCost", "premium", "total"] + }, + { + "type": "object", + "properties": { + "baseCost": { "type": "null" }, + "premium": { "type": "null" }, + "total": { "type": "null" } + }, + "required": ["baseCost", "premium", "total"] + } + ] + }, + "referral": { + "anyOf": [ + { + "type": "object", + "properties": { + "encodedReferrer": { "type": "string" }, + "decodedReferrer": { "type": "string" } + }, + "required": ["encodedReferrer", "decodedReferrer"] + }, + { + "type": "object", + "properties": { + "encodedReferrer": { "type": "null" }, + "decodedReferrer": { "type": "null" } + }, + "required": ["encodedReferrer", "decodedReferrer"] + } + ] + }, + "block": { + "type": "object", + "properties": { + "timestamp": { "type": "integer" }, + "number": { "type": "integer", "minimum": 0 } + }, + "required": ["timestamp", "number"], + "additionalProperties": false + }, + "transactionHash": { "type": "string" }, + "eventIds": { + "type": "array", + "items": { "type": "string", "minLength": 1 }, + "minItems": 1 + }, + "type": { "type": "string", "enum": ["registration"] } + }, + "required": [ + "id", + "incrementalDuration", + "registrant", + "registrationLifecycle", + "pricing", + "referral", + "block", + "transactionHash", + "eventIds", + "type" + ] + }, + { + "type": "object", + "properties": { + "id": { "type": "string", "minLength": 1 }, + "incrementalDuration": { "type": "number" }, + "registrant": { "type": "string" }, + "registrationLifecycle": { + "type": "object", + "properties": { + "subregistry": { + "type": "object", + "properties": { + "subregistryId": { + "type": "object", + "properties": { + "chainId": { + "type": "integer", + "exclusiveMinimum": 0 + }, + "address": { "type": "string" } + }, + "required": ["chainId", "address"], + "additionalProperties": false + }, + "node": { "type": "string" } + }, + "required": ["subregistryId", "node"] + }, + "node": { "type": "string" }, + "expiresAt": { "type": "integer" } + }, + "required": ["subregistry", "node", "expiresAt"] + }, + "pricing": { + "anyOf": [ + { + "type": "object", + "properties": { + "baseCost": { + "type": "object", + "properties": { + "amount": { "type": "string", "pattern": "^d+$" }, + "currency": { "type": "string", "enum": ["ETH"] } + }, + "required": ["amount", "currency"], + "additionalProperties": false + }, + "premium": { + "type": "object", + "properties": { + "amount": { "type": "string", "pattern": "^d+$" }, + "currency": { "type": "string", "enum": ["ETH"] } + }, + "required": ["amount", "currency"], + "additionalProperties": false + }, + "total": { + "type": "object", + "properties": { + "amount": { "type": "string", "pattern": "^d+$" }, + "currency": { "type": "string", "enum": ["ETH"] } + }, + "required": ["amount", "currency"], + "additionalProperties": false + } + }, + "required": ["baseCost", "premium", "total"] + }, + { + "type": "object", + "properties": { + "baseCost": { "type": "null" }, + "premium": { "type": "null" }, + "total": { "type": "null" } + }, + "required": ["baseCost", "premium", "total"] + } + ] + }, + "referral": { + "anyOf": [ + { + "type": "object", + "properties": { + "encodedReferrer": { "type": "string" }, + "decodedReferrer": { "type": "string" } + }, + "required": ["encodedReferrer", "decodedReferrer"] + }, + { + "type": "object", + "properties": { + "encodedReferrer": { "type": "null" }, + "decodedReferrer": { "type": "null" } + }, + "required": ["encodedReferrer", "decodedReferrer"] + } + ] + }, + "block": { + "type": "object", + "properties": { + "timestamp": { "type": "integer" }, + "number": { "type": "integer", "minimum": 0 } + }, + "required": ["timestamp", "number"], + "additionalProperties": false + }, + "transactionHash": { "type": "string" }, + "eventIds": { + "type": "array", + "items": { "type": "string", "minLength": 1 }, + "minItems": 1 + }, + "type": { "type": "string", "enum": ["renewal"] } + }, + "required": [ + "id", + "incrementalDuration", + "registrant", + "registrationLifecycle", + "pricing", + "referral", + "block", + "transactionHash", + "eventIds", + "type" + ] + } + ] + }, + "name": { "type": "string" } + }, + "required": ["action", "name"] + } + }, + "pageContext": { + "anyOf": [ + { + "type": "object", + "properties": { + "totalRecords": { "type": "number", "enum": [0] }, + "totalPages": { "type": "number", "enum": [1] }, + "hasNext": { "type": "boolean", "enum": [false] }, + "hasPrev": { "type": "boolean", "enum": [false] }, + "page": { "type": "integer", "exclusiveMinimum": 0 }, + "recordsPerPage": { + "type": "integer", + "exclusiveMinimum": 0, + "maximum": 100 + } + }, + "required": [ + "totalRecords", + "totalPages", + "hasNext", + "hasPrev", + "page", + "recordsPerPage" + ] + }, + { + "type": "object", + "properties": { + "totalRecords": { "type": "integer", "exclusiveMinimum": 0 }, + "totalPages": { "type": "integer", "exclusiveMinimum": 0 }, + "hasNext": { "type": "boolean" }, + "hasPrev": { "type": "boolean" }, + "startIndex": { "type": "integer", "minimum": 0 }, + "endIndex": { "type": "integer", "minimum": 0 }, + "page": { "type": "integer", "exclusiveMinimum": 0 }, + "recordsPerPage": { + "type": "integer", + "exclusiveMinimum": 0, + "maximum": 100 + } + }, + "required": [ + "totalRecords", + "totalPages", + "hasNext", + "hasPrev", + "startIndex", + "endIndex", + "page", + "recordsPerPage" + ] + } + ] + }, + "accurateAsOf": { "type": "integer" } + }, + "required": ["responseCode", "registrarActions", "pageContext"], + "example": { + "responseCode": "ok", + "registrarActions": [ + { + "action": { + "type": "registration", + "id": "0x0000000000000000000000000000000000000000000000000000000000000001", + "incrementalDuration": 31536000, + "registrant": "0xd8da6bf26964af9d7eed9e03e53415d37aa96045", + "registrationLifecycle": { + "subregistry": { + "subregistryId": { + "chainId": 1, + "address": "0x253553366da8546fc250f225fe3d25d0c782303b" + }, + "node": "0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae" + }, + "node": "0xee6c4522aab0003e8d14cd40a6af439055fd2577951148c14b6cea9a53475835", + "expiresAt": 1893456000 + }, + "pricing": { + "baseCost": { "amount": "1000000000000000", "currency": "ETH" }, + "premium": { "amount": "0", "currency": "ETH" }, + "total": { "amount": "1000000000000000", "currency": "ETH" } + }, + "referral": { "encodedReferrer": null, "decodedReferrer": null }, + "block": { "timestamp": 1700000000, "number": 18500000 }, + "transactionHash": "0x0000000000000000000000000000000000000000000000000000000000000001", + "eventIds": [ + "0x0000000000000000000000000000000000000000000000000000000000000001" + ] + }, + "name": "vitalik.eth" + } + ], + "pageContext": { + "page": 1, + "recordsPerPage": 25, + "totalRecords": 1, + "totalPages": 1, + "hasNext": false, + "hasPrev": false, + "startIndex": 0, + "endIndex": 0 + }, + "accurateAsOf": 1700000000 + } + } + } + } + }, + "400": { + "description": "Invalid input", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { "message": { "type": "string" }, "details": {} }, + "required": ["message"] + } + } + } + }, + "500": { "description": "Internal server error" } + } + } + }, + "/ensanalytics/referrers": { + "get": { + "operationId": "getReferrerLeaderboard", + "tags": ["ENSAwards"], + "summary": "Get Referrer Leaderboard", + "description": "Returns a paginated page from the referrer leaderboard", + "parameters": [ + { + "schema": { + "type": "integer", + "minimum": 1, + "description": "Page number for pagination" + }, + "required": false, + "description": "Page number for pagination", + "name": "page", + "in": "query" + }, + { + "schema": { + "type": "integer", + "minimum": 1, + "maximum": 100, + "description": "Number of referrers per page" + }, + "required": false, + "description": "Number of referrers per page", + "name": "recordsPerPage", + "in": "query" + } + ], + "responses": { + "200": { "description": "Successfully retrieved referrer leaderboard page" }, + "500": { "description": "Internal server error" } + } + } + }, + "/ensanalytics/referrers/{referrer}": { + "get": { + "operationId": "getReferrerDetail", + "tags": ["ENSAwards"], + "summary": "Get Referrer Detail", + "description": "Returns detailed information for a specific referrer by address", + "parameters": [ + { + "schema": { "type": "string", "description": "Referrer Ethereum address" }, + "required": true, + "description": "Referrer Ethereum address", + "name": "referrer", + "in": "path" + } + ], + "responses": { + "200": { "description": "Successfully retrieved referrer detail" }, + "500": { "description": "Internal server error" }, + "503": { "description": "Service unavailable - referrer leaderboard data not yet cached" } + } + } + }, + "/v1/ensanalytics/referral-leaderboard": { + "get": { + "operationId": "getReferralLeaderboard_v1", + "tags": ["ENSAwards"], + "summary": "Get Referrer Leaderboard (v1)", + "description": "Returns a paginated page from the referrer leaderboard for a specific edition", + "parameters": [ + { + "schema": { "type": "string", "minLength": 1, "pattern": "^[a-z0-9]+(-[a-z0-9]+)*$" }, + "required": true, + "name": "edition", + "in": "query" + }, + { + "schema": { + "type": "integer", + "minimum": 1, + "description": "Page number for pagination" + }, + "required": false, + "description": "Page number for pagination", + "name": "page", + "in": "query" + }, + { + "schema": { + "type": "integer", + "minimum": 1, + "maximum": 100, + "description": "Number of referrers per page" + }, + "required": false, + "description": "Number of referrers per page", + "name": "recordsPerPage", + "in": "query" + } + ], + "responses": { + "200": { "description": "Successfully retrieved referrer leaderboard page" }, + "404": { "description": "Unknown edition slug" }, + "500": { "description": "Internal server error" }, + "503": { "description": "Service unavailable" } + } + } + }, + "/v1/ensanalytics/referrer/{referrer}": { + "get": { + "operationId": "getReferrerDetail_v1", "tags": ["ENSAwards"], - "summary": "Get Referrer Detail for Editions", + "summary": "Get Referrer Detail for Editions (v1)", "description": "Returns detailed information for a specific referrer for the requested editions. Requires 1-20 distinct edition slugs. All requested editions must be recognized and have cached data, or the request fails.", "parameters": [ { @@ -2019,9 +3043,9 @@ }, "/v1/ensanalytics/editions": { "get": { - "operationId": "getEditions", + "operationId": "getEditions_v1", "tags": ["ENSAwards"], - "summary": "Get Edition Summaries", + "summary": "Get Edition Summaries (v1)", "description": "Returns a summary for each configured referral program edition, including its current status and award-model-specific runtime data. Editions are sorted in descending order by start timestamp (most recent first).", "responses": { "200": { "description": "Successfully retrieved edition summaries." }, diff --git a/package.json b/package.json index bacf366d6d..7699bb7785 100644 --- a/package.json +++ b/package.json @@ -58,6 +58,7 @@ "yauzl@<3.2.1": "^3.2.1", "fast-xml-parser@>=5.0.0 <5.5.7": ">=5.5.7", "kysely@>=0.26.0 <0.28.14": ">=0.28.14", + "defu@<=6.1.4": ">=6.1.5", "h3@<1.15.9": ">=1.15.9", "yaml@>=2.0.0 <2.8.3": ">=2.8.3", "picomatch@<2.3.2": "^2.3.2", diff --git a/packages/ensnode-sdk/src/ensapi/api/name-tokens/examples.ts b/packages/ensnode-sdk/src/ensapi/api/name-tokens/examples.ts new file mode 100644 index 0000000000..b703ca3bcf --- /dev/null +++ b/packages/ensnode-sdk/src/ensapi/api/name-tokens/examples.ts @@ -0,0 +1,39 @@ +import type { InterpretedName } from "enssdk"; + +import type { SerializedNameTokensResponseOk } from "./serialized-response"; + +/** + * Example value for {@link SerializedNameTokensResponseOk}, for use in OpenAPI documentation. + * + * - domainId and tokenId correspond to "vitalik.eth" + * - contract is the ENS BaseRegistrar (ERC-721) on Ethereum mainnet + */ +export const nameTokensResponseOkExample = { + responseCode: "ok", + registeredNameTokens: { + domainId: "0xee6c4522aab0003e8d14cd40a6af439055fd2577951148c14b6cea9a53475835", + name: "vitalik.eth" as InterpretedName, + tokens: [ + { + token: { + assetNamespace: "erc721", + contract: { + chainId: 1, + address: "0x57f1887a8bf19b14fc0df6fd9b2acc9af147ea85", + }, + tokenId: "0xaf2caa1c2ca1d027f1ac823b529d0a67cd144264b2789fa2ea4d63a67c7103cc", + }, + ownership: { + ownershipType: "fully-onchain", + owner: { + chainId: 1, + address: "0x220866b1a2219f40e72f5c628b65d54268ca3a9d", + }, + }, + mintStatus: "minted", + }, + ], + expiresAt: 2461152330, + accurateAsOf: 1700000000, + }, +} satisfies SerializedNameTokensResponseOk; diff --git a/packages/ensnode-sdk/src/ensapi/api/name-tokens/zod-schemas.test.ts b/packages/ensnode-sdk/src/ensapi/api/name-tokens/zod-schemas.test.ts index 90e6ce9c7d..d6e82d6958 100644 --- a/packages/ensnode-sdk/src/ensapi/api/name-tokens/zod-schemas.test.ts +++ b/packages/ensnode-sdk/src/ensapi/api/name-tokens/zod-schemas.test.ts @@ -3,6 +3,7 @@ import { describe, expect, it } from "vitest"; import { NFTMintStatuses } from "../../../tokenscope/assets"; import { NameTokenOwnershipTypes } from "../../../tokenscope/name-token"; +import { nameTokensResponseOkExample } from "./examples"; import { NameTokensResponseCodes, type NameTokensResponseOk } from "./response"; import type { SerializedNameTokensResponseOk } from "./serialized-response"; import { makeNameTokensResponseSchema } from "./zod-schemas"; @@ -56,6 +57,14 @@ const responseOk = { } satisfies SerializedNameTokensResponseOk; describe("Name Tokens: Zod Schemas", () => { + it("nameTokensResponseOkExample passes schema", () => { + expect( + makeNameTokensResponseSchema("Name Tokens Response", true).safeParse( + nameTokensResponseOkExample, + ).success, + ).toBe(true); + }); + it("can parse response OK correctly", () => { const schema = makeNameTokensResponseSchema(); const parsed = schema.safeParse(responseOk); diff --git a/packages/ensnode-sdk/src/ensapi/api/registrar-actions/examples.ts b/packages/ensnode-sdk/src/ensapi/api/registrar-actions/examples.ts new file mode 100644 index 0000000000..188a517a16 --- /dev/null +++ b/packages/ensnode-sdk/src/ensapi/api/registrar-actions/examples.ts @@ -0,0 +1,56 @@ +import type { InterpretedName } from "enssdk"; + +import type { SerializedRegistrarActionsResponseOk } from "./serialized-response"; + +/** + * Example value for {@link SerializedRegistrarActionsResponseOk}, for use in OpenAPI documentation. + * + * - registrationLifecycle.node is namehash("vitalik.eth") + * - subregistry.node is namehash("eth") + * - subregistry.subregistryId is the ETH Registrar Controller on Ethereum mainnet + */ +export const registrarActionsResponseOkExample = { + responseCode: "ok", + registrarActions: [ + { + action: { + type: "registration", + id: "0x0000000000000000000000000000000000000000000000000000000000000001", + incrementalDuration: 31536000, + registrant: "0xd8da6bf26964af9d7eed9e03e53415d37aa96045", + registrationLifecycle: { + subregistry: { + subregistryId: { + chainId: 1, + address: "0x253553366da8546fc250f225fe3d25d0c782303b", + }, + node: "0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae", + }, + node: "0xee6c4522aab0003e8d14cd40a6af439055fd2577951148c14b6cea9a53475835", + expiresAt: 1893456000, + }, + pricing: { + baseCost: { amount: "1000000000000000", currency: "ETH" }, + premium: { amount: "0", currency: "ETH" }, + total: { amount: "1000000000000000", currency: "ETH" }, + }, + referral: { encodedReferrer: null, decodedReferrer: null }, + block: { timestamp: 1700000000, number: 18500000 }, + transactionHash: "0x0000000000000000000000000000000000000000000000000000000000000001", + eventIds: ["0x0000000000000000000000000000000000000000000000000000000000000001"], + }, + name: "vitalik.eth" as InterpretedName, + }, + ], + pageContext: { + page: 1, + recordsPerPage: 25, + totalRecords: 1, + totalPages: 1, + hasNext: false, + hasPrev: false, + startIndex: 0, + endIndex: 0, + }, + accurateAsOf: 1700000000, +} satisfies SerializedRegistrarActionsResponseOk; diff --git a/packages/ensnode-sdk/src/ensapi/api/registrar-actions/serialize.ts b/packages/ensnode-sdk/src/ensapi/api/registrar-actions/serialize.ts index 6b8287957a..11d8815f1d 100644 --- a/packages/ensnode-sdk/src/ensapi/api/registrar-actions/serialize.ts +++ b/packages/ensnode-sdk/src/ensapi/api/registrar-actions/serialize.ts @@ -3,10 +3,13 @@ import { type NamedRegistrarAction, type RegistrarActionsResponse, RegistrarActionsResponseCodes, + type RegistrarActionsResponseError, + type RegistrarActionsResponseOk, } from "./response"; import type { SerializedNamedRegistrarAction, SerializedRegistrarActionsResponse, + SerializedRegistrarActionsResponseError, SerializedRegistrarActionsResponseOk, } from "./serialized-response"; @@ -20,6 +23,12 @@ export function serializeNamedRegistrarAction({ }; } +export function serializeRegistrarActionsResponse( + response: RegistrarActionsResponseOk, +): SerializedRegistrarActionsResponseOk; +export function serializeRegistrarActionsResponse( + response: RegistrarActionsResponseError, +): SerializedRegistrarActionsResponseError; export function serializeRegistrarActionsResponse( response: RegistrarActionsResponse, ): SerializedRegistrarActionsResponse { diff --git a/packages/ensnode-sdk/src/ensapi/api/registrar-actions/zod-schemas.test.ts b/packages/ensnode-sdk/src/ensapi/api/registrar-actions/zod-schemas.test.ts index 33c6b868fa..31aba8396c 100644 --- a/packages/ensnode-sdk/src/ensapi/api/registrar-actions/zod-schemas.test.ts +++ b/packages/ensnode-sdk/src/ensapi/api/registrar-actions/zod-schemas.test.ts @@ -1,132 +1,32 @@ -import { asInterpretedName } from "enssdk"; import { describe, expect, it } from "vitest"; +import { registrarActionsResponseOkExample } from "./examples"; import { RegistrarActionsResponseCodes, type RegistrarActionsResponseError } from "./response"; -import type { - SerializedNamedRegistrarAction, - SerializedRegistrarActionsResponseError, - SerializedRegistrarActionsResponseOk, -} from "./serialized-response"; +import type { SerializedRegistrarActionsResponseError } from "./serialized-response"; import { makeRegistrarActionsResponseSchema } from "./zod-schemas"; describe("ENSNode API Schema", () => { describe("Registrar Actions API", () => { - const validNamedRegistrarActionNormalizedWithReferral = { - action: { - id: "176209761600000000111551110000000009545322000000000000006750000000000000067", - type: "registration", - incrementalDuration: 2419200, - registrant: "0x877dd7fa7a6813361de23552c12d25af4a89cda7", - registrationLifecycle: { - subregistry: { - subregistryId: { - chainId: 11155111, - address: "0x57f1887a8bf19b14fc0df6fd9b2acc9af147ea85", - }, - node: "0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae", - }, - node: "0x5bcdea30f2d591f5357045b89d3470d4ba4da00fd344a32fe323ab6fa2c0f343", - expiresAt: 1764516816, - }, - pricing: { - baseCost: { - currency: "ETH", - amount: "7671232876711824", - }, - premium: { - currency: "ETH", - amount: "0", - }, - total: { - currency: "ETH", - amount: "7671232876711824", - }, - }, - referral: { - encodedReferrer: "0x0000000000000000000000007bddd635be34bcf860d5f02ae53b16fcd17e8f6f", - decodedReferrer: "0x7bddd635be34bcf860d5f02ae53b16fcd17e8f6f", - }, - block: { - number: 9545322, - timestamp: 1762097616, - }, - transactionHash: "0x8b3316e97a92ea0f676943a206ef1722b90b279c0a769456a89b2afe37f205fa", - eventIds: [ - "176209761600000000111551110000000009545322000000000000006750000000000000067", - "176209761600000000111551110000000009545322000000000000006750000000000000071", - ], - }, - name: asInterpretedName("nh35.eth"), - } satisfies SerializedNamedRegistrarAction; - - const validNamedRegistrarActionEncodedLabelHash = { - action: { - id: "176234701200000000111551110000000009566045000000000000014150000000000000198", - type: "registration", - incrementalDuration: 31536000, - registrant: "0x5505957ff5927f29eacabbbe8a304968bf2dc064", - registrationLifecycle: { - subregistry: { - subregistryId: { - chainId: 11155111, - address: "0x57f1887a8bf19b14fc0df6fd9b2acc9af147ea85", - }, - node: "0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae", - }, - node: "0xf1c0e6aa95596e0199f3a6341cdbe055b64ba6041662465e577ed80c4dfac2af", - expiresAt: 1793883012, - }, - pricing: { - baseCost: null, - premium: null, - total: null, - }, - referral: { - encodedReferrer: null, - decodedReferrer: null, - }, - block: { - number: 9566045, - timestamp: 1762347012, - }, - transactionHash: "0xa71cf08102ae1f634b22349dac8dc158fe96ae74008b5e24cfcda8587e056d53", - eventIds: ["176234701200000000111551110000000009566045000000000000014150000000000000198"], - }, - name: asInterpretedName( - "[e4310bf4547cb18b16b5348881d24a66d61fa94a013e5636b730b86ee64a3923].eth", - ), - } satisfies SerializedNamedRegistrarAction; - - const validResponseOk = { - responseCode: RegistrarActionsResponseCodes.Ok, - registrarActions: [ - validNamedRegistrarActionEncodedLabelHash, - validNamedRegistrarActionNormalizedWithReferral, - ], - pageContext: { - page: 1, - recordsPerPage: 10, - totalRecords: 2, - totalPages: 1, - hasNext: false, - hasPrev: false, - startIndex: 0, - endIndex: 1, - }, - accurateAsOf: 1767718566, - } satisfies SerializedRegistrarActionsResponseOk; - const validResponseError = { responseCode: RegistrarActionsResponseCodes.Error, error: { message: "any message", details: "any details" }, } satisfies SerializedRegistrarActionsResponseError; - it("can parse valid ResponseOk object", () => { - expect(() => makeRegistrarActionsResponseSchema().parse(validResponseOk)).not.toThrowError(); + it("registrarActionsResponseOkExample passes schema", () => { + expect( + makeRegistrarActionsResponseSchema().safeParse(registrarActionsResponseOkExample).success, + ).toBe(true); + }); + + it("can deserialize ResponseOk object via domain schema", () => { + const serialized = makeRegistrarActionsResponseSchema().parse( + registrarActionsResponseOkExample, + ); + expect(() => makeRegistrarActionsResponseSchema().parse(serialized)).not.toThrowError(); }); it("rejects ResponseOk object missing required accurateAsOf", () => { - const { accurateAsOf: _accurateAsOf, ...invalidResponseOk } = validResponseOk; + const { accurateAsOf: _accurateAsOf, ...invalidResponseOk } = registrarActionsResponseOkExample; expect(() => makeRegistrarActionsResponseSchema().parse(invalidResponseOk)).toThrowError(); }); diff --git a/packages/ensnode-sdk/src/ensapi/api/resolution/examples.ts b/packages/ensnode-sdk/src/ensapi/api/resolution/examples.ts new file mode 100644 index 0000000000..f65cf9c825 --- /dev/null +++ b/packages/ensnode-sdk/src/ensapi/api/resolution/examples.ts @@ -0,0 +1,39 @@ +/** + * Example values for {@link ResolveRecordsResponse}, for use in OpenAPI documentation. + */ +export const resolveRecordsResponseExample = { + records: { + name: "vitalik.eth", + addresses: { "60": "0xd8da6bf26964af9d7eed9e03e53415d37aa96045" }, + texts: { + description: "mi pinxe lo crino tcati", + }, + }, + accelerationRequested: false, + accelerationAttempted: false, +}; + +/** + * Example values for {@link ResolvePrimaryNameResponse}, for use in OpenAPI documentation. + */ +export const resolvePrimaryNameResponseExample = { + name: "jesse.base.eth", + accelerationRequested: false, + accelerationAttempted: false, +}; + +/** + * Example values for {@link ResolvePrimaryNamesResponse}, for use in OpenAPI documentation. + */ +export const resolvePrimaryNamesResponseExample = { + names: { + "1": "jesse.base.eth", + "10": null, + "8453": "jesse.base.eth", + "42161": null, + "59144": null, + "534352": null, + }, + accelerationRequested: false, + accelerationAttempted: false, +}; diff --git a/packages/ensnode-sdk/src/ensapi/api/resolution/zod-schemas.test.ts b/packages/ensnode-sdk/src/ensapi/api/resolution/zod-schemas.test.ts new file mode 100644 index 0000000000..fdc3f99950 --- /dev/null +++ b/packages/ensnode-sdk/src/ensapi/api/resolution/zod-schemas.test.ts @@ -0,0 +1,32 @@ +import { describe, expect, it } from "vitest"; + +import { + resolvePrimaryNameResponseExample, + resolvePrimaryNamesResponseExample, + resolveRecordsResponseExample, +} from "./examples"; +import { + makeResolvePrimaryNameResponseSchema, + makeResolvePrimaryNamesResponseSchema, + makeResolveRecordsResponseSchema, +} from "./zod-schemas"; + +describe("Resolution: Zod Schemas", () => { + it("resolveRecordsResponseExample passes schema", () => { + expect( + makeResolveRecordsResponseSchema().safeParse(resolveRecordsResponseExample).success, + ).toBe(true); + }); + + it("resolvePrimaryNameResponseExample passes schema", () => { + expect( + makeResolvePrimaryNameResponseSchema().safeParse(resolvePrimaryNameResponseExample).success, + ).toBe(true); + }); + + it("resolvePrimaryNamesResponseExample passes schema", () => { + expect( + makeResolvePrimaryNamesResponseSchema().safeParse(resolvePrimaryNamesResponseExample).success, + ).toBe(true); + }); +}); diff --git a/packages/ensnode-sdk/src/ensapi/api/shared/pagination/response.ts b/packages/ensnode-sdk/src/ensapi/api/shared/pagination/response.ts index 5f48dc4e24..56ca305223 100644 --- a/packages/ensnode-sdk/src/ensapi/api/shared/pagination/response.ts +++ b/packages/ensnode-sdk/src/ensapi/api/shared/pagination/response.ts @@ -24,12 +24,12 @@ export interface ResponsePageContextWithNoRecords extends Required { diff --git a/packages/ensnode-sdk/src/ensapi/api/shared/pagination/zod-schemas.ts b/packages/ensnode-sdk/src/ensapi/api/shared/pagination/zod-schemas.ts index 9e314e721b..fc90f0f2ab 100644 --- a/packages/ensnode-sdk/src/ensapi/api/shared/pagination/zod-schemas.ts +++ b/packages/ensnode-sdk/src/ensapi/api/shared/pagination/zod-schemas.ts @@ -36,8 +36,6 @@ export const makeResponsePageContextSchemaWithNoRecords = ( totalPages: z.literal(1), hasNext: z.literal(false), hasPrev: z.literal(false), - startIndex: z.undefined(), - endIndex: z.undefined(), }) .extend(makeRequestPageParamsSchema(valueLabel).shape); diff --git a/packages/ensnode-sdk/src/internal.ts b/packages/ensnode-sdk/src/internal.ts index 163ff75451..80b4058825 100644 --- a/packages/ensnode-sdk/src/internal.ts +++ b/packages/ensnode-sdk/src/internal.ts @@ -13,8 +13,11 @@ */ export * from "./ensapi/api/indexing-status/zod-schemas"; +export * from "./ensapi/api/name-tokens/examples"; export * from "./ensapi/api/name-tokens/zod-schemas"; +export * from "./ensapi/api/registrar-actions/examples"; export * from "./ensapi/api/registrar-actions/zod-schemas"; +export * from "./ensapi/api/resolution/examples"; export * from "./ensapi/api/resolution/zod-schemas"; export * from "./ensapi/api/shared/errors/zod-schemas"; export * from "./ensapi/api/shared/pagination/zod-schemas"; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6ff61b9103..e9b31d25bd 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -14,7 +14,7 @@ catalogs: version: 6.0.2 '@hono/node-server': specifier: ^1.19.13 - version: 1.19.13 + version: 1.19.14 '@ponder/client': specifier: 0.16.6 version: 0.16.6 @@ -121,13 +121,13 @@ overrides: yauzl@<3.2.1: ^3.2.1 fast-xml-parser@>=5.0.0 <5.5.7: '>=5.5.7' kysely@>=0.26.0 <0.28.14: '>=0.28.14' + defu@<=6.1.4: ^6.1.5 h3@<1.15.9: '>=1.15.9' yaml@>=2.0.0 <2.8.3: '>=2.8.3' picomatch@<2.3.2: ^2.3.2 picomatch@>=4.0.0 <4.0.4: '>=4.0.4' smol-toml@<1.6.1: '>=1.6.1' brace-expansion@>=4.0.0 <5.0.5: '>=5.0.5' - defu@<=6.1.4: ^6.1.5 vite@>=5.0.0 <=6.4.1: ^6.4.2 axios@<1.15.0: '>=1.15.0' follow-redirects@<1.16.0: ^1.16.0 @@ -163,16 +163,16 @@ importers: version: 7.0.0-dev.20260128.1 jsdom: specifier: ^27.0.1 - version: 27.0.1(postcss@8.5.8) + version: 27.0.1(postcss@8.5.6) tsup: specifier: 'catalog:' - version: 8.5.0(jiti@2.6.1)(postcss@8.5.8)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3) + version: 8.5.0(jiti@2.6.1)(postcss@8.5.6)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3) typescript: specifier: 'catalog:' version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.0.5(@types/debug@4.1.12)(@types/node@24.10.9)(jiti@2.6.1)(jsdom@27.0.1(postcss@8.5.8))(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.3) + version: 4.0.5(@types/debug@4.1.12)(@types/node@24.10.9)(jiti@2.6.1)(jsdom@27.0.1(postcss@8.5.6))(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.3) apps/ensadmin: dependencies: @@ -353,7 +353,7 @@ importers: version: link:../../packages/ponder-subgraph '@hono/node-server': specifier: 'catalog:' - version: 1.19.13(hono@4.12.14) + version: 1.19.14(hono@4.12.14) '@hono/otel': specifier: ^0.2.2 version: 0.2.2(hono@4.12.14) @@ -489,7 +489,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.0.5(@types/debug@4.1.12)(@types/node@24.10.9)(jiti@2.6.1)(jsdom@27.0.1(postcss@8.5.8))(lightningcss@1.30.2)(tsx@4.20.6)(yaml@2.8.3) + version: 4.0.5(@types/debug@4.1.12)(@types/node@24.10.9)(jiti@2.6.1)(jsdom@27.0.1(postcss@8.5.6))(lightningcss@1.30.2)(tsx@4.20.6)(yaml@2.8.3) apps/ensindexer: dependencies: @@ -565,7 +565,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.0.5(@types/debug@4.1.12)(@types/node@24.10.9)(jiti@2.6.1)(jsdom@27.0.1(postcss@8.5.8))(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.3) + version: 4.0.5(@types/debug@4.1.12)(@types/node@24.10.9)(jiti@2.6.1)(jsdom@27.0.1(postcss@8.5.6))(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.3) apps/ensrainbow: dependencies: @@ -580,7 +580,7 @@ importers: version: 5.0.5 '@hono/node-server': specifier: 'catalog:' - version: 1.19.13(hono@4.12.14) + version: 1.19.14(hono@4.12.14) classic-level: specifier: ^1.4.1 version: 1.4.1 @@ -632,7 +632,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.0.5(@types/debug@4.1.12)(@types/node@24.10.9)(jiti@2.6.1)(jsdom@27.0.1(postcss@8.5.8))(lightningcss@1.30.2)(tsx@4.20.6)(yaml@2.8.3) + version: 4.0.5(@types/debug@4.1.12)(@types/node@24.10.9)(jiti@2.6.1)(jsdom@27.0.1(postcss@8.5.6))(lightningcss@1.30.2)(tsx@4.20.6)(yaml@2.8.3) apps/fallback-ensapi: dependencies: @@ -654,7 +654,7 @@ importers: version: link:../../packages/shared-configs '@hono/node-server': specifier: 'catalog:' - version: 1.19.13(hono@4.12.14) + version: 1.19.14(hono@4.12.14) '@types/node': specifier: 'catalog:' version: 24.10.9 @@ -669,7 +669,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.0.5(@types/debug@4.1.12)(@types/node@24.10.9)(jiti@2.6.1)(jsdom@27.0.1(postcss@8.5.8))(lightningcss@1.30.2)(tsx@4.20.6)(yaml@2.8.3) + version: 4.0.5(@types/debug@4.1.12)(@types/node@24.10.9)(jiti@2.6.1)(jsdom@27.0.1(postcss@8.5.6))(lightningcss@1.30.2)(tsx@4.20.6)(yaml@2.8.3) docs/docs.ensnode.io: {} @@ -713,7 +713,7 @@ importers: version: 20.1.2 '@scalar/astro': specifier: ^0.2.4 - version: 0.2.4(astro@5.18.1(@types/node@24.10.9)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.59.0)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3)) + version: 0.2.9(astro@5.18.1(@types/node@24.10.9)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.59.0)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3)) '@tailwindcss/vite': specifier: ^4.1.15 version: 4.1.16(vite@7.3.2(@types/node@24.10.9)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.3)) @@ -864,7 +864,7 @@ importers: version: 24.10.9 tsup: specifier: 'catalog:' - version: 8.5.0(jiti@2.6.1)(postcss@8.5.8)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3) + version: 8.5.0(jiti@2.6.1)(postcss@8.5.6)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3) typescript: specifier: 'catalog:' version: 5.9.3 @@ -873,7 +873,7 @@ importers: version: 2.38.5(typescript@5.9.3)(zod@4.3.6) vitest: specifier: 'catalog:' - version: 4.0.5(@types/debug@4.1.12)(@types/node@24.10.9)(jiti@2.6.1)(jsdom@27.0.1(postcss@8.5.8))(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.3) + version: 4.0.5(@types/debug@4.1.12)(@types/node@24.10.9)(jiti@2.6.1)(jsdom@27.0.1(postcss@8.5.6))(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.3) packages/ens-referrals: dependencies: @@ -895,7 +895,7 @@ importers: version: 24.10.9 tsup: specifier: ^8.3.6 - version: 8.5.0(jiti@2.6.1)(postcss@8.5.8)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3) + version: 8.5.0(jiti@2.6.1)(postcss@8.5.6)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3) typescript: specifier: 'catalog:' version: 5.9.3 @@ -904,7 +904,7 @@ importers: version: 2.38.5(typescript@5.9.3)(zod@4.3.6) vitest: specifier: 'catalog:' - version: 4.0.5(@types/debug@4.1.12)(@types/node@24.10.9)(jiti@2.6.1)(jsdom@27.0.1(postcss@8.5.8))(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.3) + version: 4.0.5(@types/debug@4.1.12)(@types/node@24.10.9)(jiti@2.6.1)(jsdom@27.0.1(postcss@8.5.6))(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.3) packages/enscli: {} @@ -937,7 +937,7 @@ importers: version: 0.16.6(@opentelemetry/api@1.9.0(patch_hash=4b2adeefaf7c22f9987d0a125d69cab900719bec7ed7636648bea6947107033a))(@types/node@24.10.9)(@types/pg@8.16.0)(hono@4.12.14)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(typescript@5.9.3)(viem@2.38.5(typescript@5.9.3)(zod@4.3.6))(yaml@2.8.3)(zod@4.3.6) tsup: specifier: 'catalog:' - version: 8.5.0(jiti@2.6.1)(postcss@8.5.8)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3) + version: 8.5.0(jiti@2.6.1)(postcss@8.5.6)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3) typescript: specifier: 'catalog:' version: 5.9.3 @@ -946,7 +946,7 @@ importers: version: 2.38.5(typescript@5.9.3)(zod@4.3.6) vitest: specifier: 'catalog:' - version: 4.0.5(@types/debug@4.1.12)(@types/node@24.10.9)(jiti@2.6.1)(jsdom@27.0.1(postcss@8.5.8))(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.3) + version: 4.0.5(@types/debug@4.1.12)(@types/node@24.10.9)(jiti@2.6.1)(jsdom@27.0.1(postcss@8.5.6))(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.3) packages/enskit: dependencies: @@ -980,7 +980,7 @@ importers: version: 19.2.1 tsup: specifier: 'catalog:' - version: 8.5.0(jiti@2.6.1)(postcss@8.5.8)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3) + version: 8.5.0(jiti@2.6.1)(postcss@8.5.6)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3) typescript: specifier: 'catalog:' version: 5.9.3 @@ -989,7 +989,7 @@ importers: version: 2.38.5(typescript@5.9.3)(zod@4.3.6) vitest: specifier: 'catalog:' - version: 4.0.5(@types/debug@4.1.12)(@types/node@24.10.9)(jiti@2.6.1)(jsdom@27.0.1(postcss@8.5.8))(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.3) + version: 4.0.5(@types/debug@4.1.12)(@types/node@24.10.9)(jiti@2.6.1)(jsdom@27.0.1(postcss@8.5.6))(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.3) packages/ensnode-react: dependencies: @@ -1020,13 +1020,13 @@ importers: version: 19.2.1 tsup: specifier: ^8.3.6 - version: 8.5.0(jiti@2.6.1)(postcss@8.5.8)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3) + version: 8.5.0(jiti@2.6.1)(postcss@8.5.6)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3) typescript: specifier: 'catalog:' version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.0.5(@types/debug@4.1.12)(@types/node@24.10.9)(jiti@2.6.1)(jsdom@27.0.1(postcss@8.5.8))(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.3) + version: 4.0.5(@types/debug@4.1.12)(@types/node@24.10.9)(jiti@2.6.1)(jsdom@27.0.1(postcss@8.5.6))(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.3) packages/ensnode-sdk: dependencies: @@ -1057,7 +1057,7 @@ importers: version: 24.10.9 tsup: specifier: ^8.3.6 - version: 8.5.0(jiti@2.6.1)(postcss@8.5.8)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3) + version: 8.5.0(jiti@2.6.1)(postcss@8.5.6)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3) typescript: specifier: 'catalog:' version: 5.9.3 @@ -1066,7 +1066,7 @@ importers: version: 2.38.5(typescript@5.9.3)(zod@4.3.6) vitest: specifier: 'catalog:' - version: 4.0.5(@types/debug@4.1.12)(@types/node@24.10.9)(jiti@2.6.1)(jsdom@27.0.1(postcss@8.5.8))(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.3) + version: 4.0.5(@types/debug@4.1.12)(@types/node@24.10.9)(jiti@2.6.1)(jsdom@27.0.1(postcss@8.5.6))(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.3) packages/ensrainbow-sdk: dependencies: @@ -1085,13 +1085,13 @@ importers: version: link:../shared-configs tsup: specifier: 'catalog:' - version: 8.5.0(jiti@2.6.1)(postcss@8.5.8)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3) + version: 8.5.0(jiti@2.6.1)(postcss@8.5.6)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3) typescript: specifier: 'catalog:' version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.0.5(@types/debug@4.1.12)(@types/node@24.10.9)(jiti@2.6.1)(jsdom@27.0.1(postcss@8.5.8))(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.3) + version: 4.0.5(@types/debug@4.1.12)(@types/node@24.10.9)(jiti@2.6.1)(jsdom@27.0.1(postcss@8.5.6))(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.3) packages/enssdk: dependencies: @@ -1119,7 +1119,7 @@ importers: version: 16.11.0 tsup: specifier: 'catalog:' - version: 8.5.0(jiti@2.6.1)(postcss@8.5.8)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3) + version: 8.5.0(jiti@2.6.1)(postcss@8.5.6)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3) typescript: specifier: 'catalog:' version: 5.9.3 @@ -1128,7 +1128,7 @@ importers: version: 2.38.5(typescript@5.9.3)(zod@4.3.6) vitest: specifier: 'catalog:' - version: 4.0.5(@types/debug@4.1.12)(@types/node@24.10.9)(jiti@2.6.1)(jsdom@27.0.1(postcss@8.5.8))(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.3) + version: 4.0.5(@types/debug@4.1.12)(@types/node@24.10.9)(jiti@2.6.1)(jsdom@27.0.1(postcss@8.5.6))(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.3) packages/ensskills: {} @@ -1267,7 +1267,7 @@ importers: version: 24.10.9 tsup: specifier: 'catalog:' - version: 8.5.0(jiti@2.6.1)(postcss@8.5.8)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3) + version: 8.5.0(jiti@2.6.1)(postcss@8.5.6)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3) typescript: specifier: 'catalog:' version: 5.9.3 @@ -1276,7 +1276,7 @@ importers: version: 2.38.5(typescript@5.9.3)(zod@4.3.6) vitest: specifier: 'catalog:' - version: 4.0.5(@types/debug@4.1.12)(@types/node@24.10.9)(jiti@2.6.1)(jsdom@27.0.1(postcss@8.5.8))(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.3) + version: 4.0.5(@types/debug@4.1.12)(@types/node@24.10.9)(jiti@2.6.1)(jsdom@27.0.1(postcss@8.5.6))(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.3) zod: specifier: 'catalog:' version: 4.3.6 @@ -1322,7 +1322,7 @@ importers: version: 4.12.14 tsup: specifier: 'catalog:' - version: 8.5.0(jiti@2.6.1)(postcss@8.5.8)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3) + version: 8.5.0(jiti@2.6.1)(postcss@8.5.6)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3) typescript: specifier: 'catalog:' version: 5.9.3 @@ -1630,11 +1630,6 @@ packages: engines: {node: '>=6.0.0'} hasBin: true - '@babel/parser@7.29.2': - resolution: {integrity: sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==} - engines: {node: '>=6.0.0'} - hasBin: true - '@babel/plugin-transform-react-jsx-self@7.27.1': resolution: {integrity: sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==} engines: {node: '>=6.9.0'} @@ -1655,10 +1650,6 @@ packages: resolution: {integrity: sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==} engines: {node: '>=6.9.0'} - '@babel/runtime@7.29.2': - resolution: {integrity: sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==} - engines: {node: '>=6.9.0'} - '@babel/template@7.27.2': resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==} engines: {node: '>=6.9.0'} @@ -1671,10 +1662,6 @@ packages: resolution: {integrity: sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==} engines: {node: '>=6.9.0'} - '@babel/types@7.29.0': - resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==} - engines: {node: '>=6.9.0'} - '@balena/dockerignore@1.0.2': resolution: {integrity: sha512-wMue2Sy4GAVTk6Ic4tJVcnfdau+gx2EnG7S+uAEe+TWJFqE4YoWN4/H8MSLj4eYJKxGg26lZwboEniNiNwZQ6Q==} @@ -1952,8 +1939,8 @@ packages: cpu: [ppc64] os: [aix] - '@esbuild/aix-ppc64@0.27.7': - resolution: {integrity: sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==} + '@esbuild/aix-ppc64@0.27.4': + resolution: {integrity: sha512-cQPwL2mp2nSmHHJlCyoXgHGhbEPMrEEU5xhkcy3Hs/O7nGZqEpZ2sUtLaL9MORLtDfRvVl2/3PAuEkYZH0Ty8Q==} engines: {node: '>=18'} cpu: [ppc64] os: [aix] @@ -1970,8 +1957,8 @@ packages: cpu: [arm64] os: [android] - '@esbuild/android-arm64@0.27.7': - resolution: {integrity: sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==} + '@esbuild/android-arm64@0.27.4': + resolution: {integrity: sha512-gdLscB7v75wRfu7QSm/zg6Rx29VLdy9eTr2t44sfTW7CxwAtQghZ4ZnqHk3/ogz7xao0QAgrkradbBzcqFPasw==} engines: {node: '>=18'} cpu: [arm64] os: [android] @@ -1988,8 +1975,8 @@ packages: cpu: [arm] os: [android] - '@esbuild/android-arm@0.27.7': - resolution: {integrity: sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==} + '@esbuild/android-arm@0.27.4': + resolution: {integrity: sha512-X9bUgvxiC8CHAGKYufLIHGXPJWnr0OCdR0anD2e21vdvgCI8lIfqFbnoeOz7lBjdrAGUhqLZLcQo6MLhTO2DKQ==} engines: {node: '>=18'} cpu: [arm] os: [android] @@ -2006,8 +1993,8 @@ packages: cpu: [x64] os: [android] - '@esbuild/android-x64@0.27.7': - resolution: {integrity: sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==} + '@esbuild/android-x64@0.27.4': + resolution: {integrity: sha512-PzPFnBNVF292sfpfhiyiXCGSn9HZg5BcAz+ivBuSsl6Rk4ga1oEXAamhOXRFyMcjwr2DVtm40G65N3GLeH1Lvw==} engines: {node: '>=18'} cpu: [x64] os: [android] @@ -2024,8 +2011,8 @@ packages: cpu: [arm64] os: [darwin] - '@esbuild/darwin-arm64@0.27.7': - resolution: {integrity: sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==} + '@esbuild/darwin-arm64@0.27.4': + resolution: {integrity: sha512-b7xaGIwdJlht8ZFCvMkpDN6uiSmnxxK56N2GDTMYPr2/gzvfdQN8rTfBsvVKmIVY/X7EM+/hJKEIbbHs9oA4tQ==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] @@ -2042,8 +2029,8 @@ packages: cpu: [x64] os: [darwin] - '@esbuild/darwin-x64@0.27.7': - resolution: {integrity: sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==} + '@esbuild/darwin-x64@0.27.4': + resolution: {integrity: sha512-sR+OiKLwd15nmCdqpXMnuJ9W2kpy0KigzqScqHI3Hqwr7IXxBp3Yva+yJwoqh7rE8V77tdoheRYataNKL4QrPw==} engines: {node: '>=18'} cpu: [x64] os: [darwin] @@ -2060,8 +2047,8 @@ packages: cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-arm64@0.27.7': - resolution: {integrity: sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==} + '@esbuild/freebsd-arm64@0.27.4': + resolution: {integrity: sha512-jnfpKe+p79tCnm4GVav68A7tUFeKQwQyLgESwEAUzyxk/TJr4QdGog9sqWNcUbr/bZt/O/HXouspuQDd9JxFSw==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] @@ -2078,8 +2065,8 @@ packages: cpu: [x64] os: [freebsd] - '@esbuild/freebsd-x64@0.27.7': - resolution: {integrity: sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==} + '@esbuild/freebsd-x64@0.27.4': + resolution: {integrity: sha512-2kb4ceA/CpfUrIcTUl1wrP/9ad9Atrp5J94Lq69w7UwOMolPIGrfLSvAKJp0RTvkPPyn6CIWrNy13kyLikZRZQ==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] @@ -2096,8 +2083,8 @@ packages: cpu: [arm64] os: [linux] - '@esbuild/linux-arm64@0.27.7': - resolution: {integrity: sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==} + '@esbuild/linux-arm64@0.27.4': + resolution: {integrity: sha512-7nQOttdzVGth1iz57kxg9uCz57dxQLHWxopL6mYuYthohPKEK0vU0C3O21CcBK6KDlkYVcnDXY099HcCDXd9dA==} engines: {node: '>=18'} cpu: [arm64] os: [linux] @@ -2114,8 +2101,8 @@ packages: cpu: [arm] os: [linux] - '@esbuild/linux-arm@0.27.7': - resolution: {integrity: sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==} + '@esbuild/linux-arm@0.27.4': + resolution: {integrity: sha512-aBYgcIxX/wd5n2ys0yESGeYMGF+pv6g0DhZr3G1ZG4jMfruU9Tl1i2Z+Wnj9/KjGz1lTLCcorqE2viePZqj4Eg==} engines: {node: '>=18'} cpu: [arm] os: [linux] @@ -2132,8 +2119,8 @@ packages: cpu: [ia32] os: [linux] - '@esbuild/linux-ia32@0.27.7': - resolution: {integrity: sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==} + '@esbuild/linux-ia32@0.27.4': + resolution: {integrity: sha512-oPtixtAIzgvzYcKBQM/qZ3R+9TEUd1aNJQu0HhGyqtx6oS7qTpvjheIWBbes4+qu1bNlo2V4cbkISr8q6gRBFA==} engines: {node: '>=18'} cpu: [ia32] os: [linux] @@ -2150,8 +2137,8 @@ packages: cpu: [loong64] os: [linux] - '@esbuild/linux-loong64@0.27.7': - resolution: {integrity: sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==} + '@esbuild/linux-loong64@0.27.4': + resolution: {integrity: sha512-8mL/vh8qeCoRcFH2nM8wm5uJP+ZcVYGGayMavi8GmRJjuI3g1v6Z7Ni0JJKAJW+m0EtUuARb6Lmp4hMjzCBWzA==} engines: {node: '>=18'} cpu: [loong64] os: [linux] @@ -2168,8 +2155,8 @@ packages: cpu: [mips64el] os: [linux] - '@esbuild/linux-mips64el@0.27.7': - resolution: {integrity: sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==} + '@esbuild/linux-mips64el@0.27.4': + resolution: {integrity: sha512-1RdrWFFiiLIW7LQq9Q2NES+HiD4NyT8Itj9AUeCl0IVCA459WnPhREKgwrpaIfTOe+/2rdntisegiPWn/r/aAw==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] @@ -2186,8 +2173,8 @@ packages: cpu: [ppc64] os: [linux] - '@esbuild/linux-ppc64@0.27.7': - resolution: {integrity: sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==} + '@esbuild/linux-ppc64@0.27.4': + resolution: {integrity: sha512-tLCwNG47l3sd9lpfyx9LAGEGItCUeRCWeAx6x2Jmbav65nAwoPXfewtAdtbtit/pJFLUWOhpv0FpS6GQAmPrHA==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] @@ -2204,8 +2191,8 @@ packages: cpu: [riscv64] os: [linux] - '@esbuild/linux-riscv64@0.27.7': - resolution: {integrity: sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==} + '@esbuild/linux-riscv64@0.27.4': + resolution: {integrity: sha512-BnASypppbUWyqjd1KIpU4AUBiIhVr6YlHx/cnPgqEkNoVOhHg+YiSVxM1RLfiy4t9cAulbRGTNCKOcqHrEQLIw==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] @@ -2222,8 +2209,8 @@ packages: cpu: [s390x] os: [linux] - '@esbuild/linux-s390x@0.27.7': - resolution: {integrity: sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==} + '@esbuild/linux-s390x@0.27.4': + resolution: {integrity: sha512-+eUqgb/Z7vxVLezG8bVB9SfBie89gMueS+I0xYh2tJdw3vqA/0ImZJ2ROeWwVJN59ihBeZ7Tu92dF/5dy5FttA==} engines: {node: '>=18'} cpu: [s390x] os: [linux] @@ -2240,8 +2227,8 @@ packages: cpu: [x64] os: [linux] - '@esbuild/linux-x64@0.27.7': - resolution: {integrity: sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==} + '@esbuild/linux-x64@0.27.4': + resolution: {integrity: sha512-S5qOXrKV8BQEzJPVxAwnryi2+Iq5pB40gTEIT69BQONqR7JH1EPIcQ/Uiv9mCnn05jff9umq/5nqzxlqTOg9NA==} engines: {node: '>=18'} cpu: [x64] os: [linux] @@ -2258,8 +2245,8 @@ packages: cpu: [arm64] os: [netbsd] - '@esbuild/netbsd-arm64@0.27.7': - resolution: {integrity: sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==} + '@esbuild/netbsd-arm64@0.27.4': + resolution: {integrity: sha512-xHT8X4sb0GS8qTqiwzHqpY00C95DPAq7nAwX35Ie/s+LO9830hrMd3oX0ZMKLvy7vsonee73x0lmcdOVXFzd6Q==} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] @@ -2276,8 +2263,8 @@ packages: cpu: [x64] os: [netbsd] - '@esbuild/netbsd-x64@0.27.7': - resolution: {integrity: sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==} + '@esbuild/netbsd-x64@0.27.4': + resolution: {integrity: sha512-RugOvOdXfdyi5Tyv40kgQnI0byv66BFgAqjdgtAKqHoZTbTF2QqfQrFwa7cHEORJf6X2ht+l9ABLMP0dnKYsgg==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] @@ -2294,8 +2281,8 @@ packages: cpu: [arm64] os: [openbsd] - '@esbuild/openbsd-arm64@0.27.7': - resolution: {integrity: sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==} + '@esbuild/openbsd-arm64@0.27.4': + resolution: {integrity: sha512-2MyL3IAaTX+1/qP0O1SwskwcwCoOI4kV2IBX1xYnDDqthmq5ArrW94qSIKCAuRraMgPOmG0RDTA74mzYNQA9ow==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] @@ -2312,8 +2299,8 @@ packages: cpu: [x64] os: [openbsd] - '@esbuild/openbsd-x64@0.27.7': - resolution: {integrity: sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==} + '@esbuild/openbsd-x64@0.27.4': + resolution: {integrity: sha512-u8fg/jQ5aQDfsnIV6+KwLOf1CmJnfu1ShpwqdwC0uA7ZPwFws55Ngc12vBdeUdnuWoQYx/SOQLGDcdlfXhYmXQ==} engines: {node: '>=18'} cpu: [x64] os: [openbsd] @@ -2330,8 +2317,8 @@ packages: cpu: [arm64] os: [openharmony] - '@esbuild/openharmony-arm64@0.27.7': - resolution: {integrity: sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==} + '@esbuild/openharmony-arm64@0.27.4': + resolution: {integrity: sha512-JkTZrl6VbyO8lDQO3yv26nNr2RM2yZzNrNHEsj9bm6dOwwu9OYN28CjzZkH57bh4w0I2F7IodpQvUAEd1mbWXg==} engines: {node: '>=18'} cpu: [arm64] os: [openharmony] @@ -2348,8 +2335,8 @@ packages: cpu: [x64] os: [sunos] - '@esbuild/sunos-x64@0.27.7': - resolution: {integrity: sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==} + '@esbuild/sunos-x64@0.27.4': + resolution: {integrity: sha512-/gOzgaewZJfeJTlsWhvUEmUG4tWEY2Spp5M20INYRg2ZKl9QPO3QEEgPeRtLjEWSW8FilRNacPOg8R1uaYkA6g==} engines: {node: '>=18'} cpu: [x64] os: [sunos] @@ -2366,8 +2353,8 @@ packages: cpu: [arm64] os: [win32] - '@esbuild/win32-arm64@0.27.7': - resolution: {integrity: sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==} + '@esbuild/win32-arm64@0.27.4': + resolution: {integrity: sha512-Z9SExBg2y32smoDQdf1HRwHRt6vAHLXcxD2uGgO/v2jK7Y718Ix4ndsbNMU/+1Qiem9OiOdaqitioZwxivhXYg==} engines: {node: '>=18'} cpu: [arm64] os: [win32] @@ -2384,8 +2371,8 @@ packages: cpu: [ia32] os: [win32] - '@esbuild/win32-ia32@0.27.7': - resolution: {integrity: sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==} + '@esbuild/win32-ia32@0.27.4': + resolution: {integrity: sha512-DAyGLS0Jz5G5iixEbMHi5KdiApqHBWMGzTtMiJ72ZOLhbu/bzxgAe8Ue8CTS3n3HbIUHQz/L51yMdGMeoxXNJw==} engines: {node: '>=18'} cpu: [ia32] os: [win32] @@ -2402,8 +2389,8 @@ packages: cpu: [x64] os: [win32] - '@esbuild/win32-x64@0.27.7': - resolution: {integrity: sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==} + '@esbuild/win32-x64@0.27.4': + resolution: {integrity: sha512-+knoa0BDoeXgkNvvV1vvbZX4+hizelrkwmGJBdT17t8FNPwG2lKemmuMZlmaNQ3ws3DKKCxpb4zRZEIp3UxFCg==} engines: {node: '>=18'} cpu: [x64] os: [win32] @@ -2616,8 +2603,8 @@ packages: peerDependencies: react: '>= 16 || ^19.0.0-rc' - '@hono/node-server@1.19.13': - resolution: {integrity: sha512-TsQLe4i2gvoTtrHje625ngThGBySOgSK3Xo2XRYOdqGN1teR8+I7vchQC46uLJi8OF62YTYA3AhSpumtkhsaKQ==} + '@hono/node-server@1.19.14': + resolution: {integrity: sha512-GwtvgtXxnWsucXvbQXkRgqksiH2Qed37H9xHZocE5sA3N8O8O8/8FA3uclQXxXVzc9XBZuEOMK7+r02FmSpHtw==} engines: {node: '>=18.14.1'} peerDependencies: hono: ^4 @@ -2670,6 +2657,12 @@ packages: cpu: [arm64] os: [darwin] + '@img/sharp-darwin-arm64@0.34.4': + resolution: {integrity: sha512-sitdlPzDVyvmINUdJle3TNHl+AG9QcwiAMsXmccqsCOMZNIdW2/7S26w0LyU8euiLVzFBL3dXPwVCq/ODnf2vA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [darwin] + '@img/sharp-darwin-arm64@0.34.5': resolution: {integrity: sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} @@ -2682,6 +2675,12 @@ packages: cpu: [x64] os: [darwin] + '@img/sharp-darwin-x64@0.34.4': + resolution: {integrity: sha512-rZheupWIoa3+SOdF/IcUe1ah4ZDpKBGWcsPX6MT0lYniH9micvIU7HQkYTfrx5Xi8u+YqwLtxC/3vl8TQN6rMg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [darwin] + '@img/sharp-darwin-x64@0.34.5': resolution: {integrity: sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} @@ -2693,6 +2692,11 @@ packages: cpu: [arm64] os: [darwin] + '@img/sharp-libvips-darwin-arm64@1.2.3': + resolution: {integrity: sha512-QzWAKo7kpHxbuHqUC28DZ9pIKpSi2ts2OJnoIGI26+HMgq92ZZ4vk8iJd4XsxN+tYfNJxzH6W62X5eTcsBymHw==} + cpu: [arm64] + os: [darwin] + '@img/sharp-libvips-darwin-arm64@1.2.4': resolution: {integrity: sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==} cpu: [arm64] @@ -2703,6 +2707,11 @@ packages: cpu: [x64] os: [darwin] + '@img/sharp-libvips-darwin-x64@1.2.3': + resolution: {integrity: sha512-Ju+g2xn1E2AKO6YBhxjj+ACcsPQRHT0bhpglxcEf+3uyPY+/gL8veniKoo96335ZaPo03bdDXMv0t+BBFAbmRA==} + cpu: [x64] + os: [darwin] + '@img/sharp-libvips-darwin-x64@1.2.4': resolution: {integrity: sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==} cpu: [x64] @@ -2714,6 +2723,12 @@ packages: os: [linux] libc: [glibc] + '@img/sharp-libvips-linux-arm64@1.2.3': + resolution: {integrity: sha512-I4RxkXU90cpufazhGPyVujYwfIm9Nk1QDEmiIsaPwdnm013F7RIceaCc87kAH+oUB1ezqEvC6ga4m7MSlqsJvQ==} + cpu: [arm64] + os: [linux] + libc: [glibc] + '@img/sharp-libvips-linux-arm64@1.2.4': resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==} cpu: [arm64] @@ -2726,12 +2741,24 @@ packages: os: [linux] libc: [glibc] + '@img/sharp-libvips-linux-arm@1.2.3': + resolution: {integrity: sha512-x1uE93lyP6wEwGvgAIV0gP6zmaL/a0tGzJs/BIDDG0zeBhMnuUPm7ptxGhUbcGs4okDJrk4nxgrmxpib9g6HpA==} + cpu: [arm] + os: [linux] + libc: [glibc] + '@img/sharp-libvips-linux-arm@1.2.4': resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==} cpu: [arm] os: [linux] libc: [glibc] + '@img/sharp-libvips-linux-ppc64@1.2.3': + resolution: {integrity: sha512-Y2T7IsQvJLMCBM+pmPbM3bKT/yYJvVtLJGfCs4Sp95SjvnFIjynbjzsa7dY1fRJX45FTSfDksbTp6AGWudiyCg==} + cpu: [ppc64] + os: [linux] + libc: [glibc] + '@img/sharp-libvips-linux-ppc64@1.2.4': resolution: {integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==} cpu: [ppc64] @@ -2750,6 +2777,12 @@ packages: os: [linux] libc: [glibc] + '@img/sharp-libvips-linux-s390x@1.2.3': + resolution: {integrity: sha512-RgWrs/gVU7f+K7P+KeHFaBAJlNkD1nIZuVXdQv6S+fNA6syCcoboNjsV2Pou7zNlVdNQoQUpQTk8SWDHUA3y/w==} + cpu: [s390x] + os: [linux] + libc: [glibc] + '@img/sharp-libvips-linux-s390x@1.2.4': resolution: {integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==} cpu: [s390x] @@ -2762,6 +2795,12 @@ packages: os: [linux] libc: [glibc] + '@img/sharp-libvips-linux-x64@1.2.3': + resolution: {integrity: sha512-3JU7LmR85K6bBiRzSUc/Ff9JBVIFVvq6bomKE0e63UXGeRw2HPVEjoJke1Yx+iU4rL7/7kUjES4dZ/81Qjhyxg==} + cpu: [x64] + os: [linux] + libc: [glibc] + '@img/sharp-libvips-linux-x64@1.2.4': resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==} cpu: [x64] @@ -2774,6 +2813,12 @@ packages: os: [linux] libc: [musl] + '@img/sharp-libvips-linuxmusl-arm64@1.2.3': + resolution: {integrity: sha512-F9q83RZ8yaCwENw1GieztSfj5msz7GGykG/BA+MOUefvER69K/ubgFHNeSyUu64amHIYKGDs4sRCMzXVj8sEyw==} + cpu: [arm64] + os: [linux] + libc: [musl] + '@img/sharp-libvips-linuxmusl-arm64@1.2.4': resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==} cpu: [arm64] @@ -2786,6 +2831,12 @@ packages: os: [linux] libc: [musl] + '@img/sharp-libvips-linuxmusl-x64@1.2.3': + resolution: {integrity: sha512-U5PUY5jbc45ANM6tSJpsgqmBF/VsL6LnxJmIf11kB7J5DctHgqm0SkuXzVWtIY90GnJxKnC/JT251TDnk1fu/g==} + cpu: [x64] + os: [linux] + libc: [musl] + '@img/sharp-libvips-linuxmusl-x64@1.2.4': resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==} cpu: [x64] @@ -2799,6 +2850,13 @@ packages: os: [linux] libc: [glibc] + '@img/sharp-linux-arm64@0.34.4': + resolution: {integrity: sha512-YXU1F/mN/Wu786tl72CyJjP/Ngl8mGHN1hST4BGl+hiW5jhCnV2uRVTNOcaYPs73NeT/H8Upm3y9582JVuZHrQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [linux] + libc: [glibc] + '@img/sharp-linux-arm64@0.34.5': resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} @@ -2813,6 +2871,13 @@ packages: os: [linux] libc: [glibc] + '@img/sharp-linux-arm@0.34.4': + resolution: {integrity: sha512-Xyam4mlqM0KkTHYVSuc6wXRmM7LGN0P12li03jAnZ3EJWZqj83+hi8Y9UxZUbxsgsK1qOEwg7O0Bc0LjqQVtxA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm] + os: [linux] + libc: [glibc] + '@img/sharp-linux-arm@0.34.5': resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} @@ -2820,6 +2885,13 @@ packages: os: [linux] libc: [glibc] + '@img/sharp-linux-ppc64@0.34.4': + resolution: {integrity: sha512-F4PDtF4Cy8L8hXA2p3TO6s4aDt93v+LKmpcYFLAVdkkD3hSxZzee0rh6/+94FpAynsuMpLX5h+LRsSG3rIciUQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [ppc64] + os: [linux] + libc: [glibc] + '@img/sharp-linux-ppc64@0.34.5': resolution: {integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} @@ -2841,6 +2913,13 @@ packages: os: [linux] libc: [glibc] + '@img/sharp-linux-s390x@0.34.4': + resolution: {integrity: sha512-qVrZKE9Bsnzy+myf7lFKvng6bQzhNUAYcVORq2P7bDlvmF6u2sCmK2KyEQEBdYk+u3T01pVsPrkj943T1aJAsw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [s390x] + os: [linux] + libc: [glibc] + '@img/sharp-linux-s390x@0.34.5': resolution: {integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} @@ -2855,6 +2934,13 @@ packages: os: [linux] libc: [glibc] + '@img/sharp-linux-x64@0.34.4': + resolution: {integrity: sha512-ZfGtcp2xS51iG79c6Vhw9CWqQC8l2Ot8dygxoDoIQPTat/Ov3qAa8qpxSrtAEAJW+UjTXc4yxCjNfxm4h6Xm2A==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [linux] + libc: [glibc] + '@img/sharp-linux-x64@0.34.5': resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} @@ -2869,6 +2955,13 @@ packages: os: [linux] libc: [musl] + '@img/sharp-linuxmusl-arm64@0.34.4': + resolution: {integrity: sha512-8hDVvW9eu4yHWnjaOOR8kHVrew1iIX+MUgwxSuH2XyYeNRtLUe4VNioSqbNkB7ZYQJj9rUTT4PyRscyk2PXFKA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [linux] + libc: [musl] + '@img/sharp-linuxmusl-arm64@0.34.5': resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} @@ -2883,6 +2976,13 @@ packages: os: [linux] libc: [musl] + '@img/sharp-linuxmusl-x64@0.34.4': + resolution: {integrity: sha512-lU0aA5L8QTlfKjpDCEFOZsTYGn3AEiO6db8W5aQDxj0nQkVrZWmN3ZP9sYKWJdtq3PWPhUNlqehWyXpYDcI9Sg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [linux] + libc: [musl] + '@img/sharp-linuxmusl-x64@0.34.5': resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} @@ -2895,11 +2995,22 @@ packages: engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [wasm32] + '@img/sharp-wasm32@0.34.4': + resolution: {integrity: sha512-33QL6ZO/qpRyG7woB/HUALz28WnTMI2W1jgX3Nu2bypqLIKx/QKMILLJzJjI+SIbvXdG9fUnmrxR7vbi1sTBeA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [wasm32] + '@img/sharp-wasm32@0.34.5': resolution: {integrity: sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [wasm32] + '@img/sharp-win32-arm64@0.34.4': + resolution: {integrity: sha512-2Q250do/5WXTwxW3zjsEuMSv5sUU4Tq9VThWKlU2EYLm4MB7ZeMwF+SFJutldYODXF6jzc6YEOC+VfX0SZQPqA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [win32] + '@img/sharp-win32-arm64@0.34.5': resolution: {integrity: sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} @@ -2912,6 +3023,12 @@ packages: cpu: [ia32] os: [win32] + '@img/sharp-win32-ia32@0.34.4': + resolution: {integrity: sha512-3ZeLue5V82dT92CNL6rsal6I2weKw1cYu+rGKm8fOCCtJTR2gYeUfY3FqUnIJsMUPIH68oS5jmZ0NiJ508YpEw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [ia32] + os: [win32] + '@img/sharp-win32-ia32@0.34.5': resolution: {integrity: sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} @@ -2924,6 +3041,12 @@ packages: cpu: [x64] os: [win32] + '@img/sharp-win32-x64@0.34.4': + resolution: {integrity: sha512-xIyj4wpYs8J18sVN3mSQjwrw7fKUqRw+Z5rnHNCy5fYTxigBz81u5mOMPmFumwjcn8+ld1ppptMBCLic1nz6ig==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [win32] + '@img/sharp-win32-x64@0.34.5': resolution: {integrity: sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} @@ -4067,22 +4190,22 @@ packages: cpu: [x64] os: [win32] - '@scalar/astro@0.2.4': - resolution: {integrity: sha512-5nlUsC6tbR6bOq+WT2NDbc6yOVmysZE2L9Vq9t3YgV0Q6m96sCmfmdTsWKEM82ZrqMSTVZPnOMYvPZZTXVothA==} + '@scalar/astro@0.2.9': + resolution: {integrity: sha512-KOmQMAolj3vkvHXnLmKY3kL8q2/yml2gsY0s6Mn3Wr372xPIiF+CTuWoog3z4BU+VlvEgTdpltq0cIl53yyEHg==} engines: {node: '>=22'} peerDependencies: astro: ^4.0.0 || ^5.0.0 - '@scalar/core@0.4.4': - resolution: {integrity: sha512-eXIG0opyQn45FzpTp0dAWFP1Vjcx+helgUAsa0uN36tyUR7DSmz2kRwHqqedzvPWryeRCKPz7/vwzKpETZp5lg==} + '@scalar/client-side-rendering@0.1.2': + resolution: {integrity: sha512-gX2QIc+lpErO9RWkJmt02sBZoCOjyZ7EiLLEyGj7x9UKkk7E4zj6Wbk1eUPo6i3oIwhtZ+Vybj/xzexCK2bxwA==} engines: {node: '>=22'} - '@scalar/helpers@0.4.2': - resolution: {integrity: sha512-IrgrGVSahCfYDNWITazz4Q1BOndp5eEzlimRkfxiYn++KqeWyLfALyym1omqcdKGYtiSx1KIbKaUJL9vkjaN7w==} + '@scalar/helpers@0.5.1': + resolution: {integrity: sha512-9VvPfv8b+YZVIFwR3SWeq4Y8ij/kU3/kf2M6NKcbf2iVyh63d8s0ssap5m/nOhiz/Puidv/29MAJlJCA0LRssA==} engines: {node: '>=22'} - '@scalar/types@0.7.4': - resolution: {integrity: sha512-1o9uf42lZ9YD0XP/HMWrwXN0unx6vFTTgtduA1F28Yloea9Pfv9N2R/t0wO91iSIzw4+NubEFolunbdb2QcgHA==} + '@scalar/types@0.9.1': + resolution: {integrity: sha512-3EbkhtQc+XuDE8Ur1MPosM3YTACFIFNba4M3vGulqv8jcvhMXT+KWPvGUJxq0CDvySqjGa8cE6w85v8T+MjDng==} engines: {node: '>=22'} '@scure/base@1.2.6': @@ -6181,8 +6304,8 @@ packages: engines: {node: '>=18'} hasBin: true - esbuild@0.27.7: - resolution: {integrity: sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==} + esbuild@0.27.4: + resolution: {integrity: sha512-Rq4vbHnYkK5fws5NF7MYTU68FPRE1ajX7heQ/8QXXWqNgqqJ/GkmmyxIzUnf2Sr/bakf8l54716CcMGHYhMrrQ==} engines: {node: '>=18'} hasBin: true @@ -6304,8 +6427,8 @@ packages: fast-xml-builder@1.1.4: resolution: {integrity: sha512-f2jhpN4Eccy0/Uz9csxh3Nu6q4ErKxf0XIsasomfOihuSUa3/xw6w8dnOtCDgEItQFJG8KyXPzQXzcODDrrbOg==} - fast-xml-parser@5.5.9: - resolution: {integrity: sha512-jldvxr1MC6rtiZKgrFnDSvT8xuH+eJqxqOBThUVjYrxssYTo1avZLGql5l0a0BAERR01CadYzZ83kVEkbyDg+g==} + fast-xml-parser@5.5.8: + resolution: {integrity: sha512-Z7Fh2nVQSb2d+poDViM063ix2ZGt9jmY1nWhPfHBOK2Hgnb/OW3P4Et3P/81SEej0J7QbWtJqxO05h8QYfK7LQ==} hasBin: true fastq@1.19.1: @@ -7470,8 +7593,8 @@ packages: engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true - nanoid@5.1.7: - resolution: {integrity: sha512-ua3NDgISf6jdwezAheMOk4mbE1LXjm1DfMUDMuJf4AqxLFK3ccGpgWizwa5YV7Yz9EpXwEaWoRXSb/BnV0t5dQ==} + nanoid@5.1.9: + resolution: {integrity: sha512-ZUvP7KeBLe3OZ1ypw6dI/TzYJuvHP77IM4Ry73waSQTLn8/g8rpdjfyVAh7t1/+FjBtG4lCP42MEbDxOsRpBMw==} engines: {node: ^18 || >=20} hasBin: true @@ -7962,10 +8085,6 @@ packages: resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} engines: {node: ^10 || ^12 || >=14} - postcss@8.5.8: - resolution: {integrity: sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==} - engines: {node: ^10 || ^12 || >=14} - postgres-array@2.0.0: resolution: {integrity: sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==} engines: {node: '>=4'} @@ -8435,6 +8554,10 @@ packages: resolution: {integrity: sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + sharp@0.34.4: + resolution: {integrity: sha512-FUH39xp3SBPnxWvd5iib1X8XY7J0K0X7d93sie9CJg2PO8/7gmg89Nve6OjItK53/MlAushNNxteBYfM6DEuoA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + sharp@0.34.5: resolution: {integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} @@ -8638,8 +8761,8 @@ packages: resolution: {integrity: sha512-1tB5mhVo7U+ETBKNf92xT4hrQa3pm0MZ0PQvuDnWgAAGHDsfp4lPSpiS6psrSiet87wyGPh9ft6wmhOMQ0hDiw==} engines: {node: '>=14.16'} - strnum@2.2.2: - resolution: {integrity: sha512-DnR90I+jtXNSTXWdwrEy9FakW7UX+qUZg28gj5fk2vxxl7uS/3bpI4fjFYVmdK9etptYBPNkpahuQnEwhwECqA==} + strnum@2.2.0: + resolution: {integrity: sha512-Y7Bj8XyJxnPAORMZj/xltsfo55uOiyHcU2tnAVzHUnSJR/KsEX+9RoDeXEnsXtl/CX4fAcrt64gZ13aGaWPeBg==} stubborn-fs@2.0.0: resolution: {integrity: sha512-Y0AvSwDw8y+nlSNFXMm2g6L51rBGdAQT20J3YSOqxC53Lo3bjWRtr2BKcfYoAf352WYpsZSTURrA0tqhfgudPA==} @@ -8934,8 +9057,8 @@ packages: resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} engines: {node: '>=16'} - type-fest@5.5.0: - resolution: {integrity: sha512-PlBfpQwiUvGViBNX84Yxwjsdhd1TUlXr6zjX7eoirtCPIr08NAmxwa+fcYBTeRQxHo9YC9wwF3m9i700sHma8g==} + type-fest@5.6.0: + resolution: {integrity: sha512-8ZiHFm91orbSAe2PSAiSVBVko18pbhbiB3U9GglSzF/zCGkR+rxpHx6sEMCUm4kxY4LjDIUGgCfUMtwfZfjfUA==} engines: {node: '>=20'} typesafe-path@0.2.2: @@ -9886,9 +10009,9 @@ snapshots: '@astrojs/tailwind@6.0.2(astro@5.18.1(@types/node@24.10.9)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.59.0)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3))(tailwindcss@4.1.5)(ts-node@10.9.2(@types/node@24.10.9)(typescript@5.9.3))': dependencies: astro: 5.18.1(@types/node@24.10.9)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.59.0)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3) - autoprefixer: 10.4.21(postcss@8.5.8) - postcss: 8.5.8 - postcss-load-config: 4.0.2(postcss@8.5.8)(ts-node@10.9.2(@types/node@24.10.9)(typescript@5.9.3)) + autoprefixer: 10.4.21(postcss@8.5.6) + postcss: 8.5.6 + postcss-load-config: 4.0.2(postcss@8.5.6)(ts-node@10.9.2(@types/node@24.10.9)(typescript@5.9.3)) tailwindcss: 4.1.5 transitivePeerDependencies: - ts-node @@ -10228,7 +10351,7 @@ snapshots: '@aws-sdk/xml-builder@3.972.9': dependencies: '@smithy/types': 4.13.0 - fast-xml-parser: 5.5.9 + fast-xml-parser: 5.5.8 tslib: 2.8.1 '@aws/lambda-invoke-store@0.2.3': {} @@ -10312,10 +10435,6 @@ snapshots: dependencies: '@babel/types': 7.28.5 - '@babel/parser@7.29.2': - dependencies: - '@babel/types': 7.29.0 - '@babel/plugin-transform-react-jsx-self@7.27.1(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 @@ -10330,8 +10449,6 @@ snapshots: '@babel/runtime@7.28.6': {} - '@babel/runtime@7.29.2': {} - '@babel/template@7.27.2': dependencies: '@babel/code-frame': 7.29.0 @@ -10355,11 +10472,6 @@ snapshots: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.28.5 - '@babel/types@7.29.0': - dependencies: - '@babel/helper-string-parser': 7.27.1 - '@babel/helper-validator-identifier': 7.28.5 - '@balena/dockerignore@1.0.2': {} '@biomejs/biome@2.3.2': @@ -10609,11 +10721,6 @@ snapshots: '@csstools/css-syntax-patches-for-csstree@1.0.14(postcss@8.5.6)': dependencies: postcss: 8.5.6 - optional: true - - '@csstools/css-syntax-patches-for-csstree@1.0.14(postcss@8.5.8)': - dependencies: - postcss: 8.5.8 '@csstools/css-tokenizer@3.0.4': {} @@ -10737,7 +10844,7 @@ snapshots: '@esbuild/aix-ppc64@0.27.2': optional: true - '@esbuild/aix-ppc64@0.27.7': + '@esbuild/aix-ppc64@0.27.4': optional: true '@esbuild/android-arm64@0.25.11': @@ -10746,7 +10853,7 @@ snapshots: '@esbuild/android-arm64@0.27.2': optional: true - '@esbuild/android-arm64@0.27.7': + '@esbuild/android-arm64@0.27.4': optional: true '@esbuild/android-arm@0.25.11': @@ -10755,7 +10862,7 @@ snapshots: '@esbuild/android-arm@0.27.2': optional: true - '@esbuild/android-arm@0.27.7': + '@esbuild/android-arm@0.27.4': optional: true '@esbuild/android-x64@0.25.11': @@ -10764,7 +10871,7 @@ snapshots: '@esbuild/android-x64@0.27.2': optional: true - '@esbuild/android-x64@0.27.7': + '@esbuild/android-x64@0.27.4': optional: true '@esbuild/darwin-arm64@0.25.11': @@ -10773,7 +10880,7 @@ snapshots: '@esbuild/darwin-arm64@0.27.2': optional: true - '@esbuild/darwin-arm64@0.27.7': + '@esbuild/darwin-arm64@0.27.4': optional: true '@esbuild/darwin-x64@0.25.11': @@ -10782,7 +10889,7 @@ snapshots: '@esbuild/darwin-x64@0.27.2': optional: true - '@esbuild/darwin-x64@0.27.7': + '@esbuild/darwin-x64@0.27.4': optional: true '@esbuild/freebsd-arm64@0.25.11': @@ -10791,7 +10898,7 @@ snapshots: '@esbuild/freebsd-arm64@0.27.2': optional: true - '@esbuild/freebsd-arm64@0.27.7': + '@esbuild/freebsd-arm64@0.27.4': optional: true '@esbuild/freebsd-x64@0.25.11': @@ -10800,7 +10907,7 @@ snapshots: '@esbuild/freebsd-x64@0.27.2': optional: true - '@esbuild/freebsd-x64@0.27.7': + '@esbuild/freebsd-x64@0.27.4': optional: true '@esbuild/linux-arm64@0.25.11': @@ -10809,7 +10916,7 @@ snapshots: '@esbuild/linux-arm64@0.27.2': optional: true - '@esbuild/linux-arm64@0.27.7': + '@esbuild/linux-arm64@0.27.4': optional: true '@esbuild/linux-arm@0.25.11': @@ -10818,7 +10925,7 @@ snapshots: '@esbuild/linux-arm@0.27.2': optional: true - '@esbuild/linux-arm@0.27.7': + '@esbuild/linux-arm@0.27.4': optional: true '@esbuild/linux-ia32@0.25.11': @@ -10827,7 +10934,7 @@ snapshots: '@esbuild/linux-ia32@0.27.2': optional: true - '@esbuild/linux-ia32@0.27.7': + '@esbuild/linux-ia32@0.27.4': optional: true '@esbuild/linux-loong64@0.25.11': @@ -10836,7 +10943,7 @@ snapshots: '@esbuild/linux-loong64@0.27.2': optional: true - '@esbuild/linux-loong64@0.27.7': + '@esbuild/linux-loong64@0.27.4': optional: true '@esbuild/linux-mips64el@0.25.11': @@ -10845,7 +10952,7 @@ snapshots: '@esbuild/linux-mips64el@0.27.2': optional: true - '@esbuild/linux-mips64el@0.27.7': + '@esbuild/linux-mips64el@0.27.4': optional: true '@esbuild/linux-ppc64@0.25.11': @@ -10854,7 +10961,7 @@ snapshots: '@esbuild/linux-ppc64@0.27.2': optional: true - '@esbuild/linux-ppc64@0.27.7': + '@esbuild/linux-ppc64@0.27.4': optional: true '@esbuild/linux-riscv64@0.25.11': @@ -10863,7 +10970,7 @@ snapshots: '@esbuild/linux-riscv64@0.27.2': optional: true - '@esbuild/linux-riscv64@0.27.7': + '@esbuild/linux-riscv64@0.27.4': optional: true '@esbuild/linux-s390x@0.25.11': @@ -10872,7 +10979,7 @@ snapshots: '@esbuild/linux-s390x@0.27.2': optional: true - '@esbuild/linux-s390x@0.27.7': + '@esbuild/linux-s390x@0.27.4': optional: true '@esbuild/linux-x64@0.25.11': @@ -10881,7 +10988,7 @@ snapshots: '@esbuild/linux-x64@0.27.2': optional: true - '@esbuild/linux-x64@0.27.7': + '@esbuild/linux-x64@0.27.4': optional: true '@esbuild/netbsd-arm64@0.25.11': @@ -10890,7 +10997,7 @@ snapshots: '@esbuild/netbsd-arm64@0.27.2': optional: true - '@esbuild/netbsd-arm64@0.27.7': + '@esbuild/netbsd-arm64@0.27.4': optional: true '@esbuild/netbsd-x64@0.25.11': @@ -10899,7 +11006,7 @@ snapshots: '@esbuild/netbsd-x64@0.27.2': optional: true - '@esbuild/netbsd-x64@0.27.7': + '@esbuild/netbsd-x64@0.27.4': optional: true '@esbuild/openbsd-arm64@0.25.11': @@ -10908,7 +11015,7 @@ snapshots: '@esbuild/openbsd-arm64@0.27.2': optional: true - '@esbuild/openbsd-arm64@0.27.7': + '@esbuild/openbsd-arm64@0.27.4': optional: true '@esbuild/openbsd-x64@0.25.11': @@ -10917,7 +11024,7 @@ snapshots: '@esbuild/openbsd-x64@0.27.2': optional: true - '@esbuild/openbsd-x64@0.27.7': + '@esbuild/openbsd-x64@0.27.4': optional: true '@esbuild/openharmony-arm64@0.25.11': @@ -10926,7 +11033,7 @@ snapshots: '@esbuild/openharmony-arm64@0.27.2': optional: true - '@esbuild/openharmony-arm64@0.27.7': + '@esbuild/openharmony-arm64@0.27.4': optional: true '@esbuild/sunos-x64@0.25.11': @@ -10935,7 +11042,7 @@ snapshots: '@esbuild/sunos-x64@0.27.2': optional: true - '@esbuild/sunos-x64@0.27.7': + '@esbuild/sunos-x64@0.27.4': optional: true '@esbuild/win32-arm64@0.25.11': @@ -10944,7 +11051,7 @@ snapshots: '@esbuild/win32-arm64@0.27.2': optional: true - '@esbuild/win32-arm64@0.27.7': + '@esbuild/win32-arm64@0.27.4': optional: true '@esbuild/win32-ia32@0.25.11': @@ -10953,7 +11060,7 @@ snapshots: '@esbuild/win32-ia32@0.27.2': optional: true - '@esbuild/win32-ia32@0.27.7': + '@esbuild/win32-ia32@0.27.4': optional: true '@esbuild/win32-x64@0.25.11': @@ -10962,7 +11069,7 @@ snapshots: '@esbuild/win32-x64@0.27.2': optional: true - '@esbuild/win32-x64@0.27.7': + '@esbuild/win32-x64@0.27.4': optional: true '@escape.tech/graphql-armor-max-aliases@2.6.2': @@ -11307,7 +11414,7 @@ snapshots: dependencies: react: 19.2.1 - '@hono/node-server@1.19.13(hono@4.12.14)': + '@hono/node-server@1.19.14(hono@4.12.14)': dependencies: hono: 4.12.14 @@ -11391,6 +11498,11 @@ snapshots: '@img/sharp-libvips-darwin-arm64': 1.0.4 optional: true + '@img/sharp-darwin-arm64@0.34.4': + optionalDependencies: + '@img/sharp-libvips-darwin-arm64': 1.2.3 + optional: true + '@img/sharp-darwin-arm64@0.34.5': optionalDependencies: '@img/sharp-libvips-darwin-arm64': 1.2.4 @@ -11401,6 +11513,11 @@ snapshots: '@img/sharp-libvips-darwin-x64': 1.0.4 optional: true + '@img/sharp-darwin-x64@0.34.4': + optionalDependencies: + '@img/sharp-libvips-darwin-x64': 1.2.3 + optional: true + '@img/sharp-darwin-x64@0.34.5': optionalDependencies: '@img/sharp-libvips-darwin-x64': 1.2.4 @@ -11409,27 +11526,42 @@ snapshots: '@img/sharp-libvips-darwin-arm64@1.0.4': optional: true + '@img/sharp-libvips-darwin-arm64@1.2.3': + optional: true + '@img/sharp-libvips-darwin-arm64@1.2.4': optional: true '@img/sharp-libvips-darwin-x64@1.0.4': optional: true + '@img/sharp-libvips-darwin-x64@1.2.3': + optional: true + '@img/sharp-libvips-darwin-x64@1.2.4': optional: true '@img/sharp-libvips-linux-arm64@1.0.4': optional: true + '@img/sharp-libvips-linux-arm64@1.2.3': + optional: true + '@img/sharp-libvips-linux-arm64@1.2.4': optional: true '@img/sharp-libvips-linux-arm@1.0.5': optional: true + '@img/sharp-libvips-linux-arm@1.2.3': + optional: true + '@img/sharp-libvips-linux-arm@1.2.4': optional: true + '@img/sharp-libvips-linux-ppc64@1.2.3': + optional: true + '@img/sharp-libvips-linux-ppc64@1.2.4': optional: true @@ -11439,24 +11571,36 @@ snapshots: '@img/sharp-libvips-linux-s390x@1.0.4': optional: true + '@img/sharp-libvips-linux-s390x@1.2.3': + optional: true + '@img/sharp-libvips-linux-s390x@1.2.4': optional: true '@img/sharp-libvips-linux-x64@1.0.4': optional: true + '@img/sharp-libvips-linux-x64@1.2.3': + optional: true + '@img/sharp-libvips-linux-x64@1.2.4': optional: true '@img/sharp-libvips-linuxmusl-arm64@1.0.4': optional: true + '@img/sharp-libvips-linuxmusl-arm64@1.2.3': + optional: true + '@img/sharp-libvips-linuxmusl-arm64@1.2.4': optional: true '@img/sharp-libvips-linuxmusl-x64@1.0.4': optional: true + '@img/sharp-libvips-linuxmusl-x64@1.2.3': + optional: true + '@img/sharp-libvips-linuxmusl-x64@1.2.4': optional: true @@ -11465,6 +11609,11 @@ snapshots: '@img/sharp-libvips-linux-arm64': 1.0.4 optional: true + '@img/sharp-linux-arm64@0.34.4': + optionalDependencies: + '@img/sharp-libvips-linux-arm64': 1.2.3 + optional: true + '@img/sharp-linux-arm64@0.34.5': optionalDependencies: '@img/sharp-libvips-linux-arm64': 1.2.4 @@ -11475,11 +11624,21 @@ snapshots: '@img/sharp-libvips-linux-arm': 1.0.5 optional: true + '@img/sharp-linux-arm@0.34.4': + optionalDependencies: + '@img/sharp-libvips-linux-arm': 1.2.3 + optional: true + '@img/sharp-linux-arm@0.34.5': optionalDependencies: '@img/sharp-libvips-linux-arm': 1.2.4 optional: true + '@img/sharp-linux-ppc64@0.34.4': + optionalDependencies: + '@img/sharp-libvips-linux-ppc64': 1.2.3 + optional: true + '@img/sharp-linux-ppc64@0.34.5': optionalDependencies: '@img/sharp-libvips-linux-ppc64': 1.2.4 @@ -11495,6 +11654,11 @@ snapshots: '@img/sharp-libvips-linux-s390x': 1.0.4 optional: true + '@img/sharp-linux-s390x@0.34.4': + optionalDependencies: + '@img/sharp-libvips-linux-s390x': 1.2.3 + optional: true + '@img/sharp-linux-s390x@0.34.5': optionalDependencies: '@img/sharp-libvips-linux-s390x': 1.2.4 @@ -11505,6 +11669,11 @@ snapshots: '@img/sharp-libvips-linux-x64': 1.0.4 optional: true + '@img/sharp-linux-x64@0.34.4': + optionalDependencies: + '@img/sharp-libvips-linux-x64': 1.2.3 + optional: true + '@img/sharp-linux-x64@0.34.5': optionalDependencies: '@img/sharp-libvips-linux-x64': 1.2.4 @@ -11515,6 +11684,11 @@ snapshots: '@img/sharp-libvips-linuxmusl-arm64': 1.0.4 optional: true + '@img/sharp-linuxmusl-arm64@0.34.4': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-arm64': 1.2.3 + optional: true + '@img/sharp-linuxmusl-arm64@0.34.5': optionalDependencies: '@img/sharp-libvips-linuxmusl-arm64': 1.2.4 @@ -11525,6 +11699,11 @@ snapshots: '@img/sharp-libvips-linuxmusl-x64': 1.0.4 optional: true + '@img/sharp-linuxmusl-x64@0.34.4': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-x64': 1.2.3 + optional: true + '@img/sharp-linuxmusl-x64@0.34.5': optionalDependencies: '@img/sharp-libvips-linuxmusl-x64': 1.2.4 @@ -11535,23 +11714,37 @@ snapshots: '@emnapi/runtime': 1.6.0 optional: true + '@img/sharp-wasm32@0.34.4': + dependencies: + '@emnapi/runtime': 1.6.0 + optional: true + '@img/sharp-wasm32@0.34.5': dependencies: '@emnapi/runtime': 1.9.2 optional: true + '@img/sharp-win32-arm64@0.34.4': + optional: true + '@img/sharp-win32-arm64@0.34.5': optional: true '@img/sharp-win32-ia32@0.33.5': optional: true + '@img/sharp-win32-ia32@0.34.4': + optional: true + '@img/sharp-win32-ia32@0.34.5': optional: true '@img/sharp-win32-x64@0.33.5': optional: true + '@img/sharp-win32-x64@0.34.4': + optional: true + '@img/sharp-win32-x64@0.34.5': optional: true @@ -12770,22 +12963,22 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.59.0': optional: true - '@scalar/astro@0.2.4(astro@5.18.1(@types/node@24.10.9)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.59.0)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3))': + '@scalar/astro@0.2.9(astro@5.18.1(@types/node@24.10.9)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.59.0)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3))': dependencies: - '@scalar/core': 0.4.4 + '@scalar/client-side-rendering': 0.1.2 astro: 5.18.1(@types/node@24.10.9)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.59.0)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3) - '@scalar/core@0.4.4': + '@scalar/client-side-rendering@0.1.2': dependencies: - '@scalar/types': 0.7.4 + '@scalar/types': 0.9.1 - '@scalar/helpers@0.4.2': {} + '@scalar/helpers@0.5.1': {} - '@scalar/types@0.7.4': + '@scalar/types@0.9.1': dependencies: - '@scalar/helpers': 0.4.2 - nanoid: 5.1.7 - type-fest: 5.5.0 + '@scalar/helpers': 0.5.1 + nanoid: 5.1.9 + type-fest: 5.6.0 zod: 4.3.6 '@scure/base@1.2.6': {} @@ -13316,7 +13509,7 @@ snapshots: '@testing-library/dom@10.4.1': dependencies: '@babel/code-frame': 7.29.0 - '@babel/runtime': 7.29.2 + '@babel/runtime': 7.28.6 '@types/aria-query': 5.0.4 aria-query: 5.3.0 dom-accessibility-api: 0.5.16 @@ -13326,7 +13519,7 @@ snapshots: '@testing-library/react@16.3.0(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)': dependencies: - '@babel/runtime': 7.29.2 + '@babel/runtime': 7.28.6 '@testing-library/dom': 10.4.1 react: 19.2.1 react-dom: 19.2.1(react@19.2.1) @@ -14063,7 +14256,7 @@ snapshots: dlv: 1.1.3 dset: 3.1.4 es-module-lexer: 1.7.0 - esbuild: 0.27.7 + esbuild: 0.27.4 estree-walker: 3.0.3 flattie: 1.1.1 fontace: 0.4.1 @@ -14104,7 +14297,7 @@ snapshots: zod-to-json-schema: 3.25.2(zod@3.25.76) zod-to-ts: 1.2.0(typescript@5.9.3)(zod@3.25.76) optionalDependencies: - sharp: 0.34.5 + sharp: 0.34.4 transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -14153,14 +14346,14 @@ snapshots: stubborn-fs: 2.0.0 when-exit: 2.1.5 - autoprefixer@10.4.21(postcss@8.5.8): + autoprefixer@10.4.21(postcss@8.5.6): dependencies: browserslist: 4.27.0 caniuse-lite: 1.0.30001751 fraction.js: 4.3.7 normalize-range: 0.1.2 picocolors: 1.1.1 - postcss: 8.5.8 + postcss: 8.5.6 postcss-value-parser: 4.2.0 axios@1.15.0: @@ -14656,15 +14849,6 @@ snapshots: css-tree: 3.1.0 transitivePeerDependencies: - postcss - optional: true - - cssstyle@5.3.1(postcss@8.5.8): - dependencies: - '@asamuzakjp/css-color': 4.0.5 - '@csstools/css-syntax-patches-for-csstree': 1.0.14(postcss@8.5.8) - css-tree: 3.1.0 - transitivePeerDependencies: - - postcss csstype@3.1.3: {} @@ -15163,34 +15347,34 @@ snapshots: '@esbuild/win32-ia32': 0.27.2 '@esbuild/win32-x64': 0.27.2 - esbuild@0.27.7: + esbuild@0.27.4: optionalDependencies: - '@esbuild/aix-ppc64': 0.27.7 - '@esbuild/android-arm': 0.27.7 - '@esbuild/android-arm64': 0.27.7 - '@esbuild/android-x64': 0.27.7 - '@esbuild/darwin-arm64': 0.27.7 - '@esbuild/darwin-x64': 0.27.7 - '@esbuild/freebsd-arm64': 0.27.7 - '@esbuild/freebsd-x64': 0.27.7 - '@esbuild/linux-arm': 0.27.7 - '@esbuild/linux-arm64': 0.27.7 - '@esbuild/linux-ia32': 0.27.7 - '@esbuild/linux-loong64': 0.27.7 - '@esbuild/linux-mips64el': 0.27.7 - '@esbuild/linux-ppc64': 0.27.7 - '@esbuild/linux-riscv64': 0.27.7 - '@esbuild/linux-s390x': 0.27.7 - '@esbuild/linux-x64': 0.27.7 - '@esbuild/netbsd-arm64': 0.27.7 - '@esbuild/netbsd-x64': 0.27.7 - '@esbuild/openbsd-arm64': 0.27.7 - '@esbuild/openbsd-x64': 0.27.7 - '@esbuild/openharmony-arm64': 0.27.7 - '@esbuild/sunos-x64': 0.27.7 - '@esbuild/win32-arm64': 0.27.7 - '@esbuild/win32-ia32': 0.27.7 - '@esbuild/win32-x64': 0.27.7 + '@esbuild/aix-ppc64': 0.27.4 + '@esbuild/android-arm': 0.27.4 + '@esbuild/android-arm64': 0.27.4 + '@esbuild/android-x64': 0.27.4 + '@esbuild/darwin-arm64': 0.27.4 + '@esbuild/darwin-x64': 0.27.4 + '@esbuild/freebsd-arm64': 0.27.4 + '@esbuild/freebsd-x64': 0.27.4 + '@esbuild/linux-arm': 0.27.4 + '@esbuild/linux-arm64': 0.27.4 + '@esbuild/linux-ia32': 0.27.4 + '@esbuild/linux-loong64': 0.27.4 + '@esbuild/linux-mips64el': 0.27.4 + '@esbuild/linux-ppc64': 0.27.4 + '@esbuild/linux-riscv64': 0.27.4 + '@esbuild/linux-s390x': 0.27.4 + '@esbuild/linux-x64': 0.27.4 + '@esbuild/netbsd-arm64': 0.27.4 + '@esbuild/netbsd-x64': 0.27.4 + '@esbuild/openbsd-arm64': 0.27.4 + '@esbuild/openbsd-x64': 0.27.4 + '@esbuild/openharmony-arm64': 0.27.4 + '@esbuild/sunos-x64': 0.27.4 + '@esbuild/win32-arm64': 0.27.4 + '@esbuild/win32-ia32': 0.27.4 + '@esbuild/win32-x64': 0.27.4 escalade@3.2.0: {} @@ -15329,11 +15513,11 @@ snapshots: dependencies: path-expression-matcher: 1.2.0 - fast-xml-parser@5.5.9: + fast-xml-parser@5.5.8: dependencies: fast-xml-builder: 1.1.4 path-expression-matcher: 1.2.0 - strnum: 2.2.2 + strnum: 2.2.0 fastq@1.19.1: dependencies: @@ -16059,35 +16243,6 @@ snapshots: - postcss - supports-color - utf-8-validate - optional: true - - jsdom@27.0.1(postcss@8.5.8): - dependencies: - '@asamuzakjp/dom-selector': 6.7.3 - cssstyle: 5.3.1(postcss@8.5.8) - data-urls: 6.0.0 - decimal.js: 10.6.0 - html-encoding-sniffer: 4.0.0 - http-proxy-agent: 7.0.2 - https-proxy-agent: 7.0.6 - is-potential-custom-element-name: 1.0.1 - parse5: 8.0.0 - rrweb-cssom: 0.8.0 - saxes: 6.0.0 - symbol-tree: 3.2.4 - tough-cookie: 6.0.0 - w3c-xmlserializer: 5.0.0 - webidl-conversions: 8.0.0 - whatwg-encoding: 3.1.1 - whatwg-mimetype: 4.0.0 - whatwg-url: 15.1.0 - ws: 8.18.3 - xml-name-validator: 5.0.0 - transitivePeerDependencies: - - bufferutil - - postcss - - supports-color - - utf-8-validate jsesc@3.1.0: {} @@ -16278,8 +16433,8 @@ snapshots: magicast@0.5.1: dependencies: - '@babel/parser': 7.29.2 - '@babel/types': 7.29.0 + '@babel/parser': 7.28.5 + '@babel/types': 7.28.5 source-map-js: 1.2.1 make-error@1.3.6: @@ -16891,7 +17046,7 @@ snapshots: nanoid@3.3.11: {} - nanoid@5.1.7: {} + nanoid@5.1.9: {} napi-macros@2.2.2: {} @@ -17311,7 +17466,7 @@ snapshots: '@escape.tech/graphql-armor-max-aliases': 2.6.2 '@escape.tech/graphql-armor-max-depth': 2.4.2 '@escape.tech/graphql-armor-max-tokens': 2.5.1 - '@hono/node-server': 1.19.13(hono@4.12.14) + '@hono/node-server': 1.19.14(hono@4.12.14) '@ponder/utils': 0.2.18(typescript@5.9.3)(viem@2.38.5(typescript@5.9.3)(zod@4.3.6)) abitype: 0.10.3(typescript@5.9.3)(zod@4.3.6) ansi-escapes: 7.1.1 @@ -17406,12 +17561,12 @@ snapshots: - jiti - tsx - postcss-load-config@4.0.2(postcss@8.5.8)(ts-node@10.9.2(@types/node@24.10.9)(typescript@5.9.3)): + postcss-load-config@4.0.2(postcss@8.5.6)(ts-node@10.9.2(@types/node@24.10.9)(typescript@5.9.3)): dependencies: lilconfig: 3.1.3 yaml: 2.8.3 optionalDependencies: - postcss: 8.5.8 + postcss: 8.5.6 ts-node: 10.9.2(@types/node@24.10.9)(typescript@5.9.3) postcss-load-config@5.1.0(jiti@2.6.1)(postcss@8.5.6)(tsx@4.21.0): @@ -17432,15 +17587,6 @@ snapshots: tsx: 4.21.0 yaml: 2.8.3 - postcss-load-config@6.0.1(jiti@2.6.1)(postcss@8.5.8)(tsx@4.21.0)(yaml@2.8.3): - dependencies: - lilconfig: 3.1.3 - optionalDependencies: - jiti: 2.6.1 - postcss: 8.5.8 - tsx: 4.21.0 - yaml: 2.8.3 - postcss-nested@6.2.0(postcss@8.5.6): dependencies: postcss: 8.5.6 @@ -17471,12 +17617,6 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 - postcss@8.5.8: - dependencies: - nanoid: 3.3.11 - picocolors: 1.1.1 - source-map-js: 1.2.1 - postgres-array@2.0.0: {} postgres-bytea@1.0.0: {} @@ -18082,6 +18222,36 @@ snapshots: '@img/sharp-win32-ia32': 0.33.5 '@img/sharp-win32-x64': 0.33.5 + sharp@0.34.4: + dependencies: + '@img/colour': 1.0.0 + detect-libc: 2.1.2 + semver: 7.7.3 + optionalDependencies: + '@img/sharp-darwin-arm64': 0.34.4 + '@img/sharp-darwin-x64': 0.34.4 + '@img/sharp-libvips-darwin-arm64': 1.2.3 + '@img/sharp-libvips-darwin-x64': 1.2.3 + '@img/sharp-libvips-linux-arm': 1.2.3 + '@img/sharp-libvips-linux-arm64': 1.2.3 + '@img/sharp-libvips-linux-ppc64': 1.2.3 + '@img/sharp-libvips-linux-s390x': 1.2.3 + '@img/sharp-libvips-linux-x64': 1.2.3 + '@img/sharp-libvips-linuxmusl-arm64': 1.2.3 + '@img/sharp-libvips-linuxmusl-x64': 1.2.3 + '@img/sharp-linux-arm': 0.34.4 + '@img/sharp-linux-arm64': 0.34.4 + '@img/sharp-linux-ppc64': 0.34.4 + '@img/sharp-linux-s390x': 0.34.4 + '@img/sharp-linux-x64': 0.34.4 + '@img/sharp-linuxmusl-arm64': 0.34.4 + '@img/sharp-linuxmusl-x64': 0.34.4 + '@img/sharp-wasm32': 0.34.4 + '@img/sharp-win32-arm64': 0.34.4 + '@img/sharp-win32-ia32': 0.34.4 + '@img/sharp-win32-x64': 0.34.4 + optional: true + sharp@0.34.5: dependencies: '@img/colour': 1.0.0 @@ -18333,7 +18503,7 @@ snapshots: strip-json-comments@5.0.3: {} - strnum@2.2.2: {} + strnum@2.2.0: {} stubborn-fs@2.0.0: dependencies: @@ -18634,34 +18804,6 @@ snapshots: - tsx - yaml - tsup@8.5.0(jiti@2.6.1)(postcss@8.5.8)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3): - dependencies: - bundle-require: 5.1.0(esbuild@0.25.11) - cac: 6.7.14 - chokidar: 4.0.3 - consola: 3.4.2 - debug: 4.4.3 - esbuild: 0.25.11 - fix-dts-default-cjs-exports: 1.0.1 - joycon: 3.1.1 - picocolors: 1.1.1 - postcss-load-config: 6.0.1(jiti@2.6.1)(postcss@8.5.8)(tsx@4.21.0)(yaml@2.8.3) - resolve-from: 5.0.0 - rollup: 4.59.0 - source-map: 0.8.0-beta.0 - sucrase: 3.35.0 - tinyexec: 0.3.2 - tinyglobby: 0.2.15 - tree-kill: 1.2.2 - optionalDependencies: - postcss: 8.5.8 - typescript: 5.9.3 - transitivePeerDependencies: - - jiti - - supports-color - - tsx - - yaml - tsx@4.20.6: dependencies: esbuild: 0.25.11 @@ -18688,7 +18830,7 @@ snapshots: type-fest@4.41.0: {} - type-fest@5.5.0: + type-fest@5.6.0: dependencies: tagged-tag: 1.0.0 @@ -18931,7 +19073,7 @@ snapshots: esbuild: 0.25.11 fdir: 6.5.0(picomatch@4.0.4) picomatch: 4.0.4 - postcss: 8.5.8 + postcss: 8.5.6 rollup: 4.59.0 tinyglobby: 0.2.15 optionalDependencies: @@ -18947,7 +19089,7 @@ snapshots: esbuild: 0.25.11 fdir: 6.5.0(picomatch@4.0.4) picomatch: 4.0.4 - postcss: 8.5.8 + postcss: 8.5.6 rollup: 4.59.0 tinyglobby: 0.2.15 optionalDependencies: @@ -18960,10 +19102,10 @@ snapshots: vite@7.3.2(@types/node@24.10.9)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.3): dependencies: - esbuild: 0.27.7 + esbuild: 0.27.4 fdir: 6.5.0(picomatch@4.0.4) picomatch: 4.0.4 - postcss: 8.5.8 + postcss: 8.5.6 rollup: 4.59.0 tinyglobby: 0.2.15 optionalDependencies: @@ -18978,47 +19120,7 @@ snapshots: optionalDependencies: vite: 6.4.2(@types/node@24.10.9)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.3) - vitest@4.0.5(@types/debug@4.1.12)(@types/node@24.10.9)(jiti@2.6.1)(jsdom@27.0.1(postcss@8.5.6))(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.3): - dependencies: - '@vitest/expect': 4.0.5 - '@vitest/mocker': 4.0.5(vite@6.4.2(@types/node@24.10.9)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.3)) - '@vitest/pretty-format': 4.0.5 - '@vitest/runner': 4.0.5 - '@vitest/snapshot': 4.0.5 - '@vitest/spy': 4.0.5 - '@vitest/utils': 4.0.5 - debug: 4.4.3 - es-module-lexer: 1.7.0 - expect-type: 1.2.2 - magic-string: 0.30.21 - pathe: 2.0.3 - picomatch: 4.0.4 - std-env: 3.10.0 - tinybench: 2.9.0 - tinyexec: 0.3.2 - tinyglobby: 0.2.15 - tinyrainbow: 3.0.3 - vite: 6.4.2(@types/node@24.10.9)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.3) - why-is-node-running: 2.3.0 - optionalDependencies: - '@types/debug': 4.1.12 - '@types/node': 24.10.9 - jsdom: 27.0.1(postcss@8.5.6) - transitivePeerDependencies: - - jiti - - less - - lightningcss - - msw - - sass - - sass-embedded - - stylus - - sugarss - - supports-color - - terser - - tsx - - yaml - - vitest@4.0.5(@types/debug@4.1.12)(@types/node@24.10.9)(jiti@2.6.1)(jsdom@27.0.1(postcss@8.5.8))(lightningcss@1.30.2)(tsx@4.20.6)(yaml@2.8.3): + vitest@4.0.5(@types/debug@4.1.12)(@types/node@24.10.9)(jiti@2.6.1)(jsdom@27.0.1(postcss@8.5.6))(lightningcss@1.30.2)(tsx@4.20.6)(yaml@2.8.3): dependencies: '@vitest/expect': 4.0.5 '@vitest/mocker': 4.0.5(vite@6.4.2(@types/node@24.10.9)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.3)) @@ -19043,7 +19145,7 @@ snapshots: optionalDependencies: '@types/debug': 4.1.12 '@types/node': 24.10.9 - jsdom: 27.0.1(postcss@8.5.8) + jsdom: 27.0.1(postcss@8.5.6) transitivePeerDependencies: - jiti - less @@ -19058,7 +19160,7 @@ snapshots: - tsx - yaml - vitest@4.0.5(@types/debug@4.1.12)(@types/node@24.10.9)(jiti@2.6.1)(jsdom@27.0.1(postcss@8.5.8))(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.3): + vitest@4.0.5(@types/debug@4.1.12)(@types/node@24.10.9)(jiti@2.6.1)(jsdom@27.0.1(postcss@8.5.6))(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.3): dependencies: '@vitest/expect': 4.0.5 '@vitest/mocker': 4.0.5(vite@6.4.2(@types/node@24.10.9)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.3)) @@ -19083,7 +19185,7 @@ snapshots: optionalDependencies: '@types/debug': 4.1.12 '@types/node': 24.10.9 - jsdom: 27.0.1(postcss@8.5.8) + jsdom: 27.0.1(postcss@8.5.6) transitivePeerDependencies: - jiti - less From 40e34bd5aefbc367a5cf2b05942dc3a3f143db20 Mon Sep 17 00:00:00 2001 From: sevenzing Date: Mon, 13 Apr 2026 18:36:47 +0400 Subject: [PATCH 02/21] rename nameRecord to reverseName --- .../src/lib/handlers/params.schema.test.ts | 2 +- apps/ensapi/src/lib/handlers/params.schema.ts | 12 +++++++----- docs/docs.ensnode.io/ensapi-openapi.json | 18 +++++++++--------- 3 files changed, 17 insertions(+), 15 deletions(-) diff --git a/apps/ensapi/src/lib/handlers/params.schema.test.ts b/apps/ensapi/src/lib/handlers/params.schema.test.ts index 496c4e0480..a7d3411fb4 100644 --- a/apps/ensapi/src/lib/handlers/params.schema.test.ts +++ b/apps/ensapi/src/lib/handlers/params.schema.test.ts @@ -27,7 +27,7 @@ describe("params.selection", () => { it("parses selection", () => { expect( params.selection.parse({ - name: "true", + reverseName: "true", addresses: "60,0", texts: "example,hello", }), diff --git a/apps/ensapi/src/lib/handlers/params.schema.ts b/apps/ensapi/src/lib/handlers/params.schema.ts index b3b01b2052..e37846c3f8 100644 --- a/apps/ensapi/src/lib/handlers/params.schema.ts +++ b/apps/ensapi/src/lib/handlers/params.schema.ts @@ -44,7 +44,7 @@ const trace = z const accelerate = z .optional(boolstring) .default(false) - .describe("Attempt accelerated CCIP-Read resolution using L1 data.") + .describe("Attempt Protocol Acceleration using indexed data.") .openapi({ default: false, }); @@ -63,10 +63,12 @@ const chainIdsWithoutDefaultChainId = z ); const rawSelectionParams = z.object({ - nameRecord: z + reverseName: z .string() .optional() - .describe("Whether to include the ENS name record in the response.") + .describe( + "Whether to include the reverse name record in the response, see ENSIP-19 (https://docs.ens.domains/ensip/19/#reverse-resolution)", + ) .openapi({ enum: ["true", "false"], }), @@ -86,13 +88,13 @@ const rawSelectionParams = z.object({ const selection = z .object({ - nameRecord: z.optional(boolstring), + reverseName: z.optional(boolstring), addresses: z.optional(stringarray.pipe(z.array(coinType))), texts: z.optional(stringarray), }) .transform((value, ctx) => { const selection: ResolverRecordsSelection = { - ...(value.nameRecord && { name: true }), + ...(value.reverseName && { name: true }), ...(value.addresses && { addresses: value.addresses }), ...(value.texts && { texts: value.texts }), }; diff --git a/docs/docs.ensnode.io/ensapi-openapi.json b/docs/docs.ensnode.io/ensapi-openapi.json index 11ae8efabe..f0dda3c026 100644 --- a/docs/docs.ensnode.io/ensapi-openapi.json +++ b/docs/docs.ensnode.io/ensapi-openapi.json @@ -982,12 +982,12 @@ { "schema": { "type": "string", - "description": "Whether to include the ENS name record in the response.", + "description": "Whether to include the reverse name record in the response, see ENSIP-19 (https://docs.ens.domains/ensip/19/#reverse-resolution)", "enum": ["true", "false"] }, "required": false, - "description": "Whether to include the ENS name record in the response.", - "name": "nameRecord", + "description": "Whether to include the reverse name record in the response, see ENSIP-19 (https://docs.ens.domains/ensip/19/#reverse-resolution)", + "name": "reverseName", "in": "query" }, { @@ -1024,11 +1024,11 @@ { "schema": { "type": "boolean", - "description": "Attempt accelerated CCIP-Read resolution using L1 data.", + "description": "Attempt Protocol Acceleration using indexed data.", "default": false }, "required": false, - "description": "Attempt accelerated CCIP-Read resolution using L1 data.", + "description": "Attempt Protocol Acceleration using indexed data.", "name": "accelerate", "in": "query" } @@ -1117,11 +1117,11 @@ { "schema": { "type": "boolean", - "description": "Attempt accelerated CCIP-Read resolution using L1 data.", + "description": "Attempt Protocol Acceleration using indexed data.", "default": false }, "required": false, - "description": "Attempt accelerated CCIP-Read resolution using L1 data.", + "description": "Attempt Protocol Acceleration using indexed data.", "name": "accelerate", "in": "query" } @@ -1193,11 +1193,11 @@ { "schema": { "type": "boolean", - "description": "Attempt accelerated CCIP-Read resolution using L1 data.", + "description": "Attempt Protocol Acceleration using indexed data.", "default": false }, "required": false, - "description": "Attempt accelerated CCIP-Read resolution using L1 data.", + "description": "Attempt Protocol Acceleration using indexed data.", "name": "accelerate", "in": "query" } From 54f9299cc7964714613f7a1f97faec1145d9a5ac Mon Sep 17 00:00:00 2001 From: sevenzing Date: Mon, 13 Apr 2026 18:40:19 +0400 Subject: [PATCH 03/21] Add changeset --- .changeset/fair-ghosts-poke.md | 5 +++++ .changeset/nice-foxes-cheat.md | 5 +++++ .changeset/smooth-foxes-boil.md | 5 +++++ 3 files changed, 15 insertions(+) create mode 100644 .changeset/fair-ghosts-poke.md create mode 100644 .changeset/nice-foxes-cheat.md create mode 100644 .changeset/smooth-foxes-boil.md diff --git a/.changeset/fair-ghosts-poke.md b/.changeset/fair-ghosts-poke.md new file mode 100644 index 0000000000..f787360fed --- /dev/null +++ b/.changeset/fair-ghosts-poke.md @@ -0,0 +1,5 @@ +--- +"ensapi": patch +--- + +Fixes error handling in app.onError to return correct HTTP status codes diff --git a/.changeset/nice-foxes-cheat.md b/.changeset/nice-foxes-cheat.md new file mode 100644 index 0000000000..2544bf09fe --- /dev/null +++ b/.changeset/nice-foxes-cheat.md @@ -0,0 +1,5 @@ +--- +"ensapi": patch +--- + +Add example responses to autogenerated openapi doc diff --git a/.changeset/smooth-foxes-boil.md b/.changeset/smooth-foxes-boil.md new file mode 100644 index 0000000000..a4db1c098a --- /dev/null +++ b/.changeset/smooth-foxes-boil.md @@ -0,0 +1,5 @@ +--- +"@ensnode/ensnode-sdk": patch +--- + +Moved examples to separate file to reuse them in ensapi, such that we can test examples in ensnode-sdk with schema parsing and show them in openapi docs From a92c8e27cde6b1d0eb2b8195edbfe5b8d8498871 Mon Sep 17 00:00:00 2001 From: sevenzing Date: Mon, 13 Apr 2026 20:47:46 +0400 Subject: [PATCH 04/21] revert test deletion --- package.json | 1 - .../api/registrar-actions/zod-schemas.test.ts | 119 +++++++++++++++++- 2 files changed, 117 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 7699bb7785..bacf366d6d 100644 --- a/package.json +++ b/package.json @@ -58,7 +58,6 @@ "yauzl@<3.2.1": "^3.2.1", "fast-xml-parser@>=5.0.0 <5.5.7": ">=5.5.7", "kysely@>=0.26.0 <0.28.14": ">=0.28.14", - "defu@<=6.1.4": ">=6.1.5", "h3@<1.15.9": ">=1.15.9", "yaml@>=2.0.0 <2.8.3": ">=2.8.3", "picomatch@<2.3.2": "^2.3.2", diff --git a/packages/ensnode-sdk/src/ensapi/api/registrar-actions/zod-schemas.test.ts b/packages/ensnode-sdk/src/ensapi/api/registrar-actions/zod-schemas.test.ts index 31aba8396c..5d3a3c9995 100644 --- a/packages/ensnode-sdk/src/ensapi/api/registrar-actions/zod-schemas.test.ts +++ b/packages/ensnode-sdk/src/ensapi/api/registrar-actions/zod-schemas.test.ts @@ -1,17 +1,131 @@ +import { asInterpretedName } from "enssdk"; import { describe, expect, it } from "vitest"; import { registrarActionsResponseOkExample } from "./examples"; import { RegistrarActionsResponseCodes, type RegistrarActionsResponseError } from "./response"; -import type { SerializedRegistrarActionsResponseError } from "./serialized-response"; +import type { + SerializedNamedRegistrarAction, + SerializedRegistrarActionsResponseError, + SerializedRegistrarActionsResponseOk, +} from "./serialized-response"; import { makeRegistrarActionsResponseSchema } from "./zod-schemas"; describe("ENSNode API Schema", () => { describe("Registrar Actions API", () => { + const validNamedRegistrarActionNormalizedWithReferral = { + action: { + id: "176209761600000000111551110000000009545322000000000000006750000000000000067", + type: "registration", + incrementalDuration: 2419200, + registrant: "0x877dd7fa7a6813361de23552c12d25af4a89cda7", + registrationLifecycle: { + subregistry: { + subregistryId: { + chainId: 11155111, + address: "0x57f1887a8bf19b14fc0df6fd9b2acc9af147ea85", + }, + node: "0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae", + }, + node: "0x5bcdea30f2d591f5357045b89d3470d4ba4da00fd344a32fe323ab6fa2c0f343", + expiresAt: 1764516816, + }, + pricing: { + baseCost: { + currency: "ETH", + amount: "7671232876711824", + }, + premium: { + currency: "ETH", + amount: "0", + }, + total: { + currency: "ETH", + amount: "7671232876711824", + }, + }, + referral: { + encodedReferrer: "0x0000000000000000000000007bddd635be34bcf860d5f02ae53b16fcd17e8f6f", + decodedReferrer: "0x7bddd635be34bcf860d5f02ae53b16fcd17e8f6f", + }, + block: { + number: 9545322, + timestamp: 1762097616, + }, + transactionHash: "0x8b3316e97a92ea0f676943a206ef1722b90b279c0a769456a89b2afe37f205fa", + eventIds: [ + "176209761600000000111551110000000009545322000000000000006750000000000000067", + "176209761600000000111551110000000009545322000000000000006750000000000000071", + ], + }, + name: asInterpretedName("nh35.eth"), + } satisfies SerializedNamedRegistrarAction; + + const validNamedRegistrarActionEncodedLabelHash = { + action: { + id: "176234701200000000111551110000000009566045000000000000014150000000000000198", + type: "registration", + incrementalDuration: 31536000, + registrant: "0x5505957ff5927f29eacabbbe8a304968bf2dc064", + registrationLifecycle: { + subregistry: { + subregistryId: { + chainId: 11155111, + address: "0x57f1887a8bf19b14fc0df6fd9b2acc9af147ea85", + }, + node: "0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae", + }, + node: "0xf1c0e6aa95596e0199f3a6341cdbe055b64ba6041662465e577ed80c4dfac2af", + expiresAt: 1793883012, + }, + pricing: { + baseCost: null, + premium: null, + total: null, + }, + referral: { + encodedReferrer: null, + decodedReferrer: null, + }, + block: { + number: 9566045, + timestamp: 1762347012, + }, + transactionHash: "0xa71cf08102ae1f634b22349dac8dc158fe96ae74008b5e24cfcda8587e056d53", + eventIds: ["176234701200000000111551110000000009566045000000000000014150000000000000198"], + }, + name: asInterpretedName( + "[e4310bf4547cb18b16b5348881d24a66d61fa94a013e5636b730b86ee64a3923].eth", + ), + } satisfies SerializedNamedRegistrarAction; + + const validResponseOk = { + responseCode: RegistrarActionsResponseCodes.Ok, + registrarActions: [ + validNamedRegistrarActionEncodedLabelHash, + validNamedRegistrarActionNormalizedWithReferral, + ], + pageContext: { + page: 1, + recordsPerPage: 10, + totalRecords: 2, + totalPages: 1, + hasNext: false, + hasPrev: false, + startIndex: 0, + endIndex: 1, + }, + accurateAsOf: 1767718566, + } satisfies SerializedRegistrarActionsResponseOk; + const validResponseError = { responseCode: RegistrarActionsResponseCodes.Error, error: { message: "any message", details: "any details" }, } satisfies SerializedRegistrarActionsResponseError; + it("can parse valid ResponseOk object", () => { + expect(() => makeRegistrarActionsResponseSchema().parse(validResponseOk)).not.toThrowError(); + }); + it("registrarActionsResponseOkExample passes schema", () => { expect( makeRegistrarActionsResponseSchema().safeParse(registrarActionsResponseOkExample).success, @@ -26,7 +140,8 @@ describe("ENSNode API Schema", () => { }); it("rejects ResponseOk object missing required accurateAsOf", () => { - const { accurateAsOf: _accurateAsOf, ...invalidResponseOk } = registrarActionsResponseOkExample; + const { accurateAsOf: _accurateAsOf, ...invalidResponseOk } = + registrarActionsResponseOkExample; expect(() => makeRegistrarActionsResponseSchema().parse(invalidResponseOk)).toThrowError(); }); From cfc52ecb3c4e9ba5b2269085ef4eca8e87d12f14 Mon Sep 17 00:00:00 2001 From: sevenzing Date: Mon, 13 Apr 2026 20:55:18 +0400 Subject: [PATCH 05/21] do not use reverseRecord in example --- packages/ensnode-sdk/src/ensapi/api/resolution/examples.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/ensnode-sdk/src/ensapi/api/resolution/examples.ts b/packages/ensnode-sdk/src/ensapi/api/resolution/examples.ts index f65cf9c825..3d0cd74b50 100644 --- a/packages/ensnode-sdk/src/ensapi/api/resolution/examples.ts +++ b/packages/ensnode-sdk/src/ensapi/api/resolution/examples.ts @@ -3,7 +3,6 @@ */ export const resolveRecordsResponseExample = { records: { - name: "vitalik.eth", addresses: { "60": "0xd8da6bf26964af9d7eed9e03e53415d37aa96045" }, texts: { description: "mi pinxe lo crino tcati", From e20f413eaeb908dc7bf2d3714911cb3b3ab3459f Mon Sep 17 00:00:00 2001 From: sevenzing Date: Mon, 13 Apr 2026 21:07:50 +0400 Subject: [PATCH 06/21] add error example --- .../api/explore/registrar-actions-api.routes.ts | 13 +++++++++++-- docs/docs.ensnode.io/ensapi-openapi.json | 15 ++++++++++----- .../src/ensapi/api/shared/errors/examples.ts | 8 ++++++++ .../ensapi/api/shared/errors/zod-schemas.test.ts | 10 ++++++++++ packages/ensnode-sdk/src/internal.ts | 1 + 5 files changed, 40 insertions(+), 7 deletions(-) create mode 100644 packages/ensnode-sdk/src/ensapi/api/shared/errors/examples.ts create mode 100644 packages/ensnode-sdk/src/ensapi/api/shared/errors/zod-schemas.test.ts diff --git a/apps/ensapi/src/handlers/api/explore/registrar-actions-api.routes.ts b/apps/ensapi/src/handlers/api/explore/registrar-actions-api.routes.ts index 8415002800..400fff721f 100644 --- a/apps/ensapi/src/handlers/api/explore/registrar-actions-api.routes.ts +++ b/apps/ensapi/src/handlers/api/explore/registrar-actions-api.routes.ts @@ -7,6 +7,7 @@ import { } from "@ensnode/ensnode-sdk"; import { ErrorResponseSchema, + errorResponseBadRequestExample, makeLowercaseAddressSchema, makeNodeSchema, makePositiveIntegerSchema, @@ -111,7 +112,11 @@ export const getRegistrarActionsRoute = createRoute({ }, 400: { description: "Invalid query", - content: { "application/json": { schema: ErrorResponseSchema } }, + content: { + "application/json": { + schema: ErrorResponseSchema.openapi({ example: errorResponseBadRequestExample }), + }, + }, }, 500: { description: "Internal server error", @@ -157,7 +162,11 @@ export const getRegistrarActionsByParentNodeRoute = createRoute({ }, 400: { description: "Invalid input", - content: { "application/json": { schema: ErrorResponseSchema } }, + content: { + "application/json": { + schema: ErrorResponseSchema.openapi({ example: errorResponseBadRequestExample }), + }, + }, }, 500: { description: "Internal server error", diff --git a/docs/docs.ensnode.io/ensapi-openapi.json b/docs/docs.ensnode.io/ensapi-openapi.json index f0dda3c026..37a057f0d7 100644 --- a/docs/docs.ensnode.io/ensapi-openapi.json +++ b/docs/docs.ensnode.io/ensapi-openapi.json @@ -1062,7 +1062,6 @@ "required": ["records", "accelerationRequested", "accelerationAttempted"], "example": { "records": { - "name": "vitalik.eth", "addresses": { "60": "0xd8da6bf26964af9d7eed9e03e53415d37aa96045" }, "texts": { "description": "mi pinxe lo crino tcati" } }, @@ -2340,7 +2339,7 @@ }, "accurateAsOf": { "type": "integer" } }, - "required": ["responseCode", "registrarActions", "pageContext"], + "required": ["responseCode", "registrarActions", "pageContext", "accurateAsOf"], "example": { "responseCode": "ok", "registrarActions": [ @@ -2399,7 +2398,10 @@ "schema": { "type": "object", "properties": { "message": { "type": "string" }, "details": {} }, - "required": ["message"] + "required": ["message"], + "example": { + "message": "endTimestamp must be greater than or equal to beginTimestamp" + } } } } @@ -2838,7 +2840,7 @@ }, "accurateAsOf": { "type": "integer" } }, - "required": ["responseCode", "registrarActions", "pageContext"], + "required": ["responseCode", "registrarActions", "pageContext", "accurateAsOf"], "example": { "responseCode": "ok", "registrarActions": [ @@ -2897,7 +2899,10 @@ "schema": { "type": "object", "properties": { "message": { "type": "string" }, "details": {} }, - "required": ["message"] + "required": ["message"], + "example": { + "message": "endTimestamp must be greater than or equal to beginTimestamp" + } } } } diff --git a/packages/ensnode-sdk/src/ensapi/api/shared/errors/examples.ts b/packages/ensnode-sdk/src/ensapi/api/shared/errors/examples.ts new file mode 100644 index 0000000000..529ef95bb4 --- /dev/null +++ b/packages/ensnode-sdk/src/ensapi/api/shared/errors/examples.ts @@ -0,0 +1,8 @@ +import type { ErrorResponse } from "./response"; + +/** + * Example value for {@link ErrorResponse} representing a 400 Bad Request, for use in OpenAPI documentation. + */ +export const errorResponseBadRequestExample = { + message: "endTimestamp must be greater than or equal to beginTimestamp", +} satisfies ErrorResponse; diff --git a/packages/ensnode-sdk/src/ensapi/api/shared/errors/zod-schemas.test.ts b/packages/ensnode-sdk/src/ensapi/api/shared/errors/zod-schemas.test.ts new file mode 100644 index 0000000000..4ae2fa508b --- /dev/null +++ b/packages/ensnode-sdk/src/ensapi/api/shared/errors/zod-schemas.test.ts @@ -0,0 +1,10 @@ +import { describe, expect, it } from "vitest"; + +import { errorResponseBadRequestExample } from "./examples"; +import { ErrorResponseSchema } from "./zod-schemas"; + +describe("ErrorResponseSchema", () => { + it("errorResponseBadRequestExample passes schema", () => { + expect(ErrorResponseSchema.safeParse(errorResponseBadRequestExample).success).toBe(true); + }); +}); diff --git a/packages/ensnode-sdk/src/internal.ts b/packages/ensnode-sdk/src/internal.ts index 80b4058825..08c9247c0e 100644 --- a/packages/ensnode-sdk/src/internal.ts +++ b/packages/ensnode-sdk/src/internal.ts @@ -19,6 +19,7 @@ export * from "./ensapi/api/registrar-actions/examples"; export * from "./ensapi/api/registrar-actions/zod-schemas"; export * from "./ensapi/api/resolution/examples"; export * from "./ensapi/api/resolution/zod-schemas"; +export * from "./ensapi/api/shared/errors/examples"; export * from "./ensapi/api/shared/errors/zod-schemas"; export * from "./ensapi/api/shared/pagination/zod-schemas"; export * from "./ensapi/config/zod-schemas"; From 34c8e12372221cb6e07785e0585e34a7c29073ba Mon Sep 17 00:00:00 2001 From: sevenzing Date: Tue, 14 Apr 2026 11:45:15 +0400 Subject: [PATCH 07/21] revert app.onError change --- .changeset/fair-ghosts-poke.md | 5 ----- apps/ensapi/src/app.ts | 2 +- 2 files changed, 1 insertion(+), 6 deletions(-) delete mode 100644 .changeset/fair-ghosts-poke.md diff --git a/.changeset/fair-ghosts-poke.md b/.changeset/fair-ghosts-poke.md deleted file mode 100644 index f787360fed..0000000000 --- a/.changeset/fair-ghosts-poke.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"ensapi": patch ---- - -Fixes error handling in app.onError to return correct HTTP status codes diff --git a/apps/ensapi/src/app.ts b/apps/ensapi/src/app.ts index e2b4997c56..1a6b2121b9 100644 --- a/apps/ensapi/src/app.ts +++ b/apps/ensapi/src/app.ts @@ -71,7 +71,7 @@ app.get("/health", async (c) => { // log hono errors to console app.onError((error, ctx) => { logger.error(error); - return errorResponse(ctx, error); + return errorResponse(ctx, "Internal Server Error"); }); export default app; From e87bbbf19c424e22460ba0e0681dfeb97a1b8254 Mon Sep 17 00:00:00 2001 From: sevenzing Date: Tue, 14 Apr 2026 11:53:27 +0400 Subject: [PATCH 08/21] add error examples --- .../api/explore/name-tokens-api.routes.ts | 10 +- .../explore/registrar-actions-api.routes.ts | 6 +- .../api/resolution/resolution-api.routes.ts | 58 +++++++++ docs/docs.ensnode.io/ensapi-openapi.json | 120 ++++++++++++++++-- .../src/ensapi/api/name-tokens/zod-schemas.ts | 8 +- .../api/registrar-actions/zod-schemas.ts | 4 +- .../ensapi/api/shared/errors/deserialize.ts | 4 +- .../src/ensapi/api/shared/errors/examples.ts | 35 ++++- .../src/ensapi/api/shared/errors/response.ts | 4 +- .../api/shared/errors/zod-schemas.test.ts | 6 +- .../ensapi/api/shared/errors/zod-schemas.ts | 9 +- 11 files changed, 232 insertions(+), 32 deletions(-) diff --git a/apps/ensapi/src/handlers/api/explore/name-tokens-api.routes.ts b/apps/ensapi/src/handlers/api/explore/name-tokens-api.routes.ts index 291074a30d..2c673cfb2e 100644 --- a/apps/ensapi/src/handlers/api/explore/name-tokens-api.routes.ts +++ b/apps/ensapi/src/handlers/api/explore/name-tokens-api.routes.ts @@ -1,7 +1,9 @@ import { createRoute, z } from "@hono/zod-openapi"; import { - ErrorResponseSchema, + errorResponseBadRequestExample, + errorResponseInternalServerErrorExample, + makeErrorResponseSchema, makeNameTokensResponseSchema, makeNodeSchema, nameTokensResponseOkExample, @@ -53,7 +55,7 @@ export const getNameTokensRoute = createRoute({ description: "Invalid input", content: { "application/json": { - schema: ErrorResponseSchema, + schema: makeErrorResponseSchema().openapi({ example: errorResponseBadRequestExample }), }, }, }, @@ -69,7 +71,9 @@ export const getNameTokensRoute = createRoute({ description: "Internal server error", content: { "application/json": { - schema: ErrorResponseSchema, + schema: makeErrorResponseSchema().openapi({ + example: errorResponseInternalServerErrorExample, + }), }, }, }, diff --git a/apps/ensapi/src/handlers/api/explore/registrar-actions-api.routes.ts b/apps/ensapi/src/handlers/api/explore/registrar-actions-api.routes.ts index 400fff721f..eb6a10a142 100644 --- a/apps/ensapi/src/handlers/api/explore/registrar-actions-api.routes.ts +++ b/apps/ensapi/src/handlers/api/explore/registrar-actions-api.routes.ts @@ -6,8 +6,8 @@ import { RegistrarActionsOrders, } from "@ensnode/ensnode-sdk"; import { - ErrorResponseSchema, errorResponseBadRequestExample, + makeErrorResponseSchema, makeLowercaseAddressSchema, makeNodeSchema, makePositiveIntegerSchema, @@ -114,7 +114,7 @@ export const getRegistrarActionsRoute = createRoute({ description: "Invalid query", content: { "application/json": { - schema: ErrorResponseSchema.openapi({ example: errorResponseBadRequestExample }), + schema: makeErrorResponseSchema().openapi({ example: errorResponseBadRequestExample }), }, }, }, @@ -164,7 +164,7 @@ export const getRegistrarActionsByParentNodeRoute = createRoute({ description: "Invalid input", content: { "application/json": { - schema: ErrorResponseSchema.openapi({ example: errorResponseBadRequestExample }), + schema: makeErrorResponseSchema().openapi({ example: errorResponseBadRequestExample }), }, }, }, diff --git a/apps/ensapi/src/handlers/api/resolution/resolution-api.routes.ts b/apps/ensapi/src/handlers/api/resolution/resolution-api.routes.ts index 1869f02108..cacca68320 100644 --- a/apps/ensapi/src/handlers/api/resolution/resolution-api.routes.ts +++ b/apps/ensapi/src/handlers/api/resolution/resolution-api.routes.ts @@ -1,6 +1,10 @@ import { createRoute, z } from "@hono/zod-openapi"; import { + errorResponseInternalServerErrorExample, + errorResponseInvalidAddressExample, + errorResponseInvalidNameExample, + makeErrorResponseSchema, makeResolvePrimaryNameResponseSchema, makeResolvePrimaryNamesResponseSchema, makeResolveRecordsResponseSchema, @@ -37,6 +41,24 @@ export const resolveRecordsRoute = createRoute({ }, }, }, + 400: { + description: "Invalid name or query parameters", + content: { + "application/json": { + schema: makeErrorResponseSchema().openapi({ example: errorResponseInvalidNameExample }), + }, + }, + }, + 500: { + description: "Internal server error", + content: { + "application/json": { + schema: makeErrorResponseSchema().openapi({ + example: errorResponseInternalServerErrorExample, + }), + }, + }, + }, }, }); @@ -68,6 +90,24 @@ export const resolvePrimaryNameRoute = createRoute({ }, }, }, + 400: { + description: "Invalid address or chain ID", + content: { + "application/json": { + schema: makeErrorResponseSchema().openapi({ example: errorResponseInvalidAddressExample }), + }, + }, + }, + 500: { + description: "Internal server error", + content: { + "application/json": { + schema: makeErrorResponseSchema().openapi({ + example: errorResponseInternalServerErrorExample, + }), + }, + }, + }, }, }); @@ -99,5 +139,23 @@ export const resolvePrimaryNamesRoute = createRoute({ }, }, }, + 400: { + description: "Invalid address or chain IDs", + content: { + "application/json": { + schema: makeErrorResponseSchema().openapi({ example: errorResponseInvalidAddressExample }), + }, + }, + }, + 500: { + description: "Internal server error", + content: { + "application/json": { + schema: makeErrorResponseSchema().openapi({ + example: errorResponseInternalServerErrorExample, + }), + }, + }, + }, }, }); diff --git a/docs/docs.ensnode.io/ensapi-openapi.json b/docs/docs.ensnode.io/ensapi-openapi.json index 37a057f0d7..8b6b9d7e2b 100644 --- a/docs/docs.ensnode.io/ensapi-openapi.json +++ b/docs/docs.ensnode.io/ensapi-openapi.json @@ -1071,6 +1071,44 @@ } } } + }, + "400": { + "description": "Invalid name or query parameters", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { "message": { "type": "string" }, "details": {} }, + "required": ["message"], + "example": { + "message": "Invalid Input", + "details": { + "errors": [], + "properties": { + "name": { + "errors": [ + "Must be normalized, see https://docs.ens.domains/resolution/names/" + ] + } + } + } + } + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { "message": { "type": "string" }, "details": {} }, + "required": ["message"], + "example": { "message": "Internal server error" } + } + } + } } } } @@ -1147,6 +1185,40 @@ } } } + }, + "400": { + "description": "Invalid address or chain ID", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { "message": { "type": "string" }, "details": {} }, + "required": ["message"], + "example": { + "message": "Invalid Input", + "details": { + "errors": [], + "properties": { + "address": { "errors": ["EVM address must be a valid EVM address"] } + } + } + } + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { "message": { "type": "string" }, "details": {} }, + "required": ["message"], + "example": { "message": "Internal server error" } + } + } + } } } } @@ -1233,6 +1305,40 @@ } } } + }, + "400": { + "description": "Invalid address or chain IDs", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { "message": { "type": "string" }, "details": {} }, + "required": ["message"], + "example": { + "message": "Invalid Input", + "details": { + "errors": [], + "properties": { + "address": { "errors": ["EVM address must be a valid EVM address"] } + } + } + } + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { "message": { "type": "string" }, "details": {} }, + "required": ["message"], + "example": { "message": "Internal server error" } + } + } + } } } } @@ -1497,7 +1603,8 @@ "schema": { "type": "object", "properties": { "message": { "type": "string" }, "details": {} }, - "required": ["message"] + "required": ["message"], + "example": { "message": "Error occured" } } } } @@ -1710,7 +1817,8 @@ "schema": { "type": "object", "properties": { "message": { "type": "string" }, "details": {} }, - "required": ["message"] + "required": ["message"], + "example": { "message": "Internal server error" } } } } @@ -2399,9 +2507,7 @@ "type": "object", "properties": { "message": { "type": "string" }, "details": {} }, "required": ["message"], - "example": { - "message": "endTimestamp must be greater than or equal to beginTimestamp" - } + "example": { "message": "Error occured" } } } } @@ -2900,9 +3006,7 @@ "type": "object", "properties": { "message": { "type": "string" }, "details": {} }, "required": ["message"], - "example": { - "message": "endTimestamp must be greater than or equal to beginTimestamp" - } + "example": { "message": "Error occured" } } } } diff --git a/packages/ensnode-sdk/src/ensapi/api/name-tokens/zod-schemas.ts b/packages/ensnode-sdk/src/ensapi/api/name-tokens/zod-schemas.ts index 9fc89551e4..bb69c5ddc3 100644 --- a/packages/ensnode-sdk/src/ensapi/api/name-tokens/zod-schemas.ts +++ b/packages/ensnode-sdk/src/ensapi/api/name-tokens/zod-schemas.ts @@ -8,7 +8,7 @@ import { } from "../../../shared/zod-schemas"; import { NameTokenOwnershipTypes } from "../../../tokenscope/name-token"; import { makeNameTokenSchema } from "../../../tokenscope/zod-schemas"; -import { ErrorResponseSchema } from "../shared/errors/zod-schemas"; +import { makeErrorResponseSchema } from "../shared/errors/zod-schemas"; import { NameTokensResponse, NameTokensResponseCodes, @@ -104,7 +104,7 @@ export const makeNameTokensResponseErrorNameTokensNotIndexedSchema = ( z.strictObject({ responseCode: z.literal(NameTokensResponseCodes.Error), errorCode: z.literal(NameTokensResponseErrorCodes.NameTokensNotIndexed), - error: ErrorResponseSchema, + error: makeErrorResponseSchema(), }); /** @@ -116,7 +116,7 @@ export const makeNameTokensResponseErrorEnsIndexerConfigUnsupported = ( z.strictObject({ responseCode: z.literal(NameTokensResponseCodes.Error), errorCode: z.literal(NameTokensResponseErrorCodes.EnsIndexerConfigUnsupported), - error: ErrorResponseSchema, + error: makeErrorResponseSchema(), }); /** * Schema for {@link NameTokensResponseErrorIndexingStatusUnsupported} @@ -127,7 +127,7 @@ export const makeNameTokensResponseErrorNameIndexingStatusUnsupported = ( z.strictObject({ responseCode: z.literal(NameTokensResponseCodes.Error), errorCode: z.literal(NameTokensResponseErrorCodes.IndexingStatusUnsupported), - error: ErrorResponseSchema, + error: makeErrorResponseSchema(), }); /** * Schema for {@link NameTokensResponseError} diff --git a/packages/ensnode-sdk/src/ensapi/api/registrar-actions/zod-schemas.ts b/packages/ensnode-sdk/src/ensapi/api/registrar-actions/zod-schemas.ts index 0e4b45c82c..b50c56cac3 100644 --- a/packages/ensnode-sdk/src/ensapi/api/registrar-actions/zod-schemas.ts +++ b/packages/ensnode-sdk/src/ensapi/api/registrar-actions/zod-schemas.ts @@ -4,7 +4,7 @@ import type { ParsePayload } from "zod/v4/core"; import { makeRegistrarActionSchema } from "../../../registrars/zod-schemas"; import { makeReinterpretedNameSchema, makeUnixTimestampSchema } from "../../../shared/zod-schemas"; -import { ErrorResponseSchema } from "../shared/errors/zod-schemas"; +import { makeErrorResponseSchema } from "../shared/errors/zod-schemas"; import { makeResponsePageContextSchema } from "../shared/pagination/zod-schemas"; import { type NamedRegistrarAction, RegistrarActionsResponseCodes } from "./response"; @@ -54,7 +54,7 @@ export const makeRegistrarActionsResponseErrorSchema = ( ) => z.strictObject({ responseCode: z.literal(RegistrarActionsResponseCodes.Error), - error: ErrorResponseSchema, + error: makeErrorResponseSchema(), }); /** diff --git a/packages/ensnode-sdk/src/ensapi/api/shared/errors/deserialize.ts b/packages/ensnode-sdk/src/ensapi/api/shared/errors/deserialize.ts index c96d00961d..a1c620e892 100644 --- a/packages/ensnode-sdk/src/ensapi/api/shared/errors/deserialize.ts +++ b/packages/ensnode-sdk/src/ensapi/api/shared/errors/deserialize.ts @@ -1,13 +1,13 @@ import { prettifyError } from "zod/v4"; import type { ErrorResponse } from "./response"; -import { ErrorResponseSchema } from "./zod-schemas"; +import { makeErrorResponseSchema } from "./zod-schemas"; /** * Deserialize a {@link ErrorResponse} object. */ export function deserializeErrorResponse(maybeErrorResponse: unknown): ErrorResponse { - const parsed = ErrorResponseSchema.safeParse(maybeErrorResponse); + const parsed = makeErrorResponseSchema().safeParse(maybeErrorResponse); if (parsed.error) { throw new Error(`Cannot deserialize ErrorResponse:\n${prettifyError(parsed.error)}\n`); diff --git a/packages/ensnode-sdk/src/ensapi/api/shared/errors/examples.ts b/packages/ensnode-sdk/src/ensapi/api/shared/errors/examples.ts index 529ef95bb4..959b2d529d 100644 --- a/packages/ensnode-sdk/src/ensapi/api/shared/errors/examples.ts +++ b/packages/ensnode-sdk/src/ensapi/api/shared/errors/examples.ts @@ -4,5 +4,38 @@ import type { ErrorResponse } from "./response"; * Example value for {@link ErrorResponse} representing a 400 Bad Request, for use in OpenAPI documentation. */ export const errorResponseBadRequestExample = { - message: "endTimestamp must be greater than or equal to beginTimestamp", + message: "Invalid Input", +} satisfies ErrorResponse; + +/** + * Example value for {@link ErrorResponse} representing a 400 Bad Request for an invalid ENS name, + * for use in OpenAPI documentation. + */ +export const errorResponseInvalidNameExample = { + message: "Invalid Input", + details: { + errors: [], + properties: { + name: { errors: ["Must be normalized, see https://docs.ens.domains/resolution/names/"] }, + }, + }, +} satisfies ErrorResponse; + +/** + * Example value for {@link ErrorResponse} representing a 400 Bad Request for an invalid address, + * for use in OpenAPI documentation. + */ +export const errorResponseInvalidAddressExample = { + message: "Invalid Input", + details: { + errors: [], + properties: { address: { errors: ["EVM address must be a valid EVM address"] } }, + }, +} satisfies ErrorResponse; + +/** + * Example value for {@link ErrorResponse} representing a 500 Internal Server Error, for use in OpenAPI documentation. + */ +export const errorResponseInternalServerErrorExample = { + message: "Internal server error", } satisfies ErrorResponse; diff --git a/packages/ensnode-sdk/src/ensapi/api/shared/errors/response.ts b/packages/ensnode-sdk/src/ensapi/api/shared/errors/response.ts index 20a7f1b223..5bd401e4d9 100644 --- a/packages/ensnode-sdk/src/ensapi/api/shared/errors/response.ts +++ b/packages/ensnode-sdk/src/ensapi/api/shared/errors/response.ts @@ -1,8 +1,8 @@ import type { z } from "zod/v4"; -import type { ErrorResponseSchema } from "./zod-schemas"; +import type { makeErrorResponseSchema } from "./zod-schemas"; /** * API Error Response Type */ -export type ErrorResponse = z.infer; +export type ErrorResponse = z.infer>; diff --git a/packages/ensnode-sdk/src/ensapi/api/shared/errors/zod-schemas.test.ts b/packages/ensnode-sdk/src/ensapi/api/shared/errors/zod-schemas.test.ts index 4ae2fa508b..703bf6b3b3 100644 --- a/packages/ensnode-sdk/src/ensapi/api/shared/errors/zod-schemas.test.ts +++ b/packages/ensnode-sdk/src/ensapi/api/shared/errors/zod-schemas.test.ts @@ -1,10 +1,10 @@ import { describe, expect, it } from "vitest"; import { errorResponseBadRequestExample } from "./examples"; -import { ErrorResponseSchema } from "./zod-schemas"; +import { makeErrorResponseSchema } from "./zod-schemas"; -describe("ErrorResponseSchema", () => { +describe("makeErrorResponseSchema", () => { it("errorResponseBadRequestExample passes schema", () => { - expect(ErrorResponseSchema.safeParse(errorResponseBadRequestExample).success).toBe(true); + expect(makeErrorResponseSchema().safeParse(errorResponseBadRequestExample).success).toBe(true); }); }); diff --git a/packages/ensnode-sdk/src/ensapi/api/shared/errors/zod-schemas.ts b/packages/ensnode-sdk/src/ensapi/api/shared/errors/zod-schemas.ts index bdaddfdd22..50401fb9da 100644 --- a/packages/ensnode-sdk/src/ensapi/api/shared/errors/zod-schemas.ts +++ b/packages/ensnode-sdk/src/ensapi/api/shared/errors/zod-schemas.ts @@ -5,7 +5,8 @@ import type { ErrorResponse } from "./response"; /** * Schema for {@link ErrorResponse}. */ -export const ErrorResponseSchema = z.object({ - message: z.string(), - details: z.optional(z.unknown()), -}); +export const makeErrorResponseSchema = () => + z.object({ + message: z.string(), + details: z.optional(z.unknown()), + }); From 3fc9eeb99710ad88c9311605a6c76553ac440668 Mon Sep 17 00:00:00 2001 From: sevenzing Date: Tue, 14 Apr 2026 12:24:21 +0400 Subject: [PATCH 09/21] add success 200 status for typecheck --- apps/ensapi/src/handlers/api/resolution/resolution-api.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/apps/ensapi/src/handlers/api/resolution/resolution-api.ts b/apps/ensapi/src/handlers/api/resolution/resolution-api.ts index edfce56114..b864731b4e 100644 --- a/apps/ensapi/src/handlers/api/resolution/resolution-api.ts +++ b/apps/ensapi/src/handlers/api/resolution/resolution-api.ts @@ -64,7 +64,7 @@ app.openapi(resolveRecordsRoute, async (c) => { ...(showTrace && { trace }), } satisfies ResolveRecordsResponse; - return c.json(response); + return c.json(response, 200); }); /** @@ -96,7 +96,7 @@ app.openapi(resolvePrimaryNameRoute, async (c) => { ...(showTrace && { trace }), } satisfies ResolvePrimaryNameResponse; - return c.json(response); + return c.json(response, 200); }); /** @@ -112,7 +112,6 @@ app.openapi(resolvePrimaryNamesRoute, async (c) => { const { address } = c.req.valid("param"); const { chainIds, trace: showTrace, accelerate } = c.req.valid("query"); const canAccelerate = c.var.canAccelerate; - const { result, trace } = await runWithTrace(() => resolvePrimaryNames(address, chainIds, { accelerate, canAccelerate }), ); @@ -125,7 +124,7 @@ app.openapi(resolvePrimaryNamesRoute, async (c) => { ...(showTrace && { trace }), } satisfies ResolvePrimaryNamesResponse; - return c.json(response); + return c.json(response, 200); }); export default app; From 1f06ae63ccae8eb6d0bac3951d14450436920986 Mon Sep 17 00:00:00 2001 From: sevenzing Date: Tue, 14 Apr 2026 13:13:14 +0400 Subject: [PATCH 10/21] add more examples for errors --- .../api/explore/name-tokens-api.routes.ts | 12 +++++-- .../handlers/api/explore/name-tokens-api.ts | 12 ++++--- docs/docs.ensnode.io/ensapi-openapi.json | 6 ++-- .../src/ensapi/api/name-tokens/examples.ts | 32 ++++++++++++++++++- .../src/ensapi/api/name-tokens/serialize.ts | 6 ++++ .../api/name-tokens/zod-schemas.test.ts | 20 ++++++++++-- .../src/ensapi/api/shared/errors/examples.ts | 3 +- 7 files changed, 77 insertions(+), 14 deletions(-) diff --git a/apps/ensapi/src/handlers/api/explore/name-tokens-api.routes.ts b/apps/ensapi/src/handlers/api/explore/name-tokens-api.routes.ts index 2c673cfb2e..e3b918f00a 100644 --- a/apps/ensapi/src/handlers/api/explore/name-tokens-api.routes.ts +++ b/apps/ensapi/src/handlers/api/explore/name-tokens-api.routes.ts @@ -4,9 +4,13 @@ import { errorResponseBadRequestExample, errorResponseInternalServerErrorExample, makeErrorResponseSchema, + makeNameTokensResponseErrorNameTokensNotIndexedSchema, + makeNameTokensResponseErrorSchema, makeNameTokensResponseSchema, makeNodeSchema, + nameTokensNotIndexedExample, nameTokensResponseOkExample, + nameTokensServiceUnavailableExample, } from "@ensnode/ensnode-sdk/internal"; import { params } from "@/lib/handlers/params.schema"; @@ -63,7 +67,9 @@ export const getNameTokensRoute = createRoute({ description: "Name tokens not indexed", content: { "application/json": { - schema: makeNameTokensResponseSchema("Name Tokens Response", true), + schema: makeNameTokensResponseErrorNameTokensNotIndexedSchema().openapi({ + example: nameTokensNotIndexedExample, + }), }, }, }, @@ -82,7 +88,9 @@ export const getNameTokensRoute = createRoute({ "Service unavailable - Name Tokens API prerequisites not met (indexing status not ready or required plugins not activated)", content: { "application/json": { - schema: makeNameTokensResponseSchema("Name Tokens Response", true), + schema: makeNameTokensResponseErrorSchema().openapi({ + example: nameTokensServiceUnavailableExample, + }), }, }, }, diff --git a/apps/ensapi/src/handlers/api/explore/name-tokens-api.ts b/apps/ensapi/src/handlers/api/explore/name-tokens-api.ts index 7e5a733771..4c96521dcb 100644 --- a/apps/ensapi/src/handlers/api/explore/name-tokens-api.ts +++ b/apps/ensapi/src/handlers/api/explore/name-tokens-api.ts @@ -11,7 +11,9 @@ import { type NameTokensRequest, NameTokensResponseCodes, NameTokensResponseErrorCodes, + type NameTokensResponseErrorIndexingStatusUnsupported, type NameTokensResponseErrorNameTokensNotIndexed, + type NameTokensResponseOk, type PluginName, serializeNameTokensResponse, } from "@ensnode/ensnode-sdk"; @@ -59,7 +61,7 @@ app.openapi(getNameTokensRoute, async (c) => { details: "Indexing status has not yet reached the required state to enable the Name Tokens API.", }, - }), + } satisfies NameTokensResponseErrorIndexingStatusUnsupported), 503, ); } @@ -78,7 +80,7 @@ app.openapi(getNameTokensRoute, async (c) => { makeNameTokensNotIndexedResponse( `The 'name' param must not be ENS Root, no tokens exist for it.`, ), - ), + ) satisfies NameTokensResponseErrorNameTokensNotIndexed, 404, ); } @@ -95,7 +97,7 @@ app.openapi(getNameTokensRoute, async (c) => { makeNameTokensNotIndexedResponse( `This ENSNode instance has not been configured to index tokens for the requested name: '${name}'`, ), - ), + ) satisfies NameTokensResponseErrorNameTokensNotIndexed, 404, ); } @@ -125,7 +127,7 @@ app.openapi(getNameTokensRoute, async (c) => { makeNameTokensNotIndexedResponse( `No Name Tokens were indexed by this ENSNode instance for the requested ${errorMessageSubject}.`, ), - ), + ) satisfies NameTokensResponseErrorNameTokensNotIndexed, 404, ); } @@ -134,7 +136,7 @@ app.openapi(getNameTokensRoute, async (c) => { serializeNameTokensResponse({ responseCode: NameTokensResponseCodes.Ok, registeredNameTokens, - }), + } satisfies NameTokensResponseOk), 200, ); }); diff --git a/docs/docs.ensnode.io/ensapi-openapi.json b/docs/docs.ensnode.io/ensapi-openapi.json index 8b6b9d7e2b..c770aaf2f0 100644 --- a/docs/docs.ensnode.io/ensapi-openapi.json +++ b/docs/docs.ensnode.io/ensapi-openapi.json @@ -1604,7 +1604,7 @@ "type": "object", "properties": { "message": { "type": "string" }, "details": {} }, "required": ["message"], - "example": { "message": "Error occured" } + "example": { "message": "Invalid Input" } } } } @@ -2507,7 +2507,7 @@ "type": "object", "properties": { "message": { "type": "string" }, "details": {} }, "required": ["message"], - "example": { "message": "Error occured" } + "example": { "message": "Invalid Input" } } } } @@ -3006,7 +3006,7 @@ "type": "object", "properties": { "message": { "type": "string" }, "details": {} }, "required": ["message"], - "example": { "message": "Error occured" } + "example": { "message": "Invalid Input" } } } } diff --git a/packages/ensnode-sdk/src/ensapi/api/name-tokens/examples.ts b/packages/ensnode-sdk/src/ensapi/api/name-tokens/examples.ts index b703ca3bcf..1c065956f0 100644 --- a/packages/ensnode-sdk/src/ensapi/api/name-tokens/examples.ts +++ b/packages/ensnode-sdk/src/ensapi/api/name-tokens/examples.ts @@ -1,6 +1,9 @@ import type { InterpretedName } from "enssdk"; -import type { SerializedNameTokensResponseOk } from "./serialized-response"; +import type { + SerializedNameTokensResponseError, + SerializedNameTokensResponseOk, +} from "./serialized-response"; /** * Example value for {@link SerializedNameTokensResponseOk}, for use in OpenAPI documentation. @@ -37,3 +40,30 @@ export const nameTokensResponseOkExample = { accurateAsOf: 1700000000, }, } satisfies SerializedNameTokensResponseOk; + +/** + * Example value for {@link SerializedNameTokensResponseError} representing a 503 Service Unavailable + * when the Name Tokens API prerequisites are not met, for use in OpenAPI documentation. + */ +export const nameTokensServiceUnavailableExample = { + responseCode: "error", + errorCode: "unsupported-ensindexer-config", + error: { + message: "Name Tokens API is not available", + details: "Connected ENSIndexer must have all following plugins active: registrars, tokenscope", + }, +} satisfies SerializedNameTokensResponseError; + +/** + * Example value for {@link NameTokensResponseErrorNameTokensNotIndexed} representing a 404 Not Found + * when no name tokens are indexed for the requested name or domainId, for use in OpenAPI documentation. + */ +export const nameTokensNotIndexedExample = { + responseCode: "error", + errorCode: "name-tokens-not-indexed", + error: { + message: "No indexed Name Tokens found", + details: + "This ENSNode instance has not been configured to index tokens for the requested name: 'vitalik.eth'", + }, +} satisfies SerializedNameTokensResponseError; diff --git a/packages/ensnode-sdk/src/ensapi/api/name-tokens/serialize.ts b/packages/ensnode-sdk/src/ensapi/api/name-tokens/serialize.ts index abf4bf8ff4..0bccf378bc 100644 --- a/packages/ensnode-sdk/src/ensapi/api/name-tokens/serialize.ts +++ b/packages/ensnode-sdk/src/ensapi/api/name-tokens/serialize.ts @@ -2,6 +2,8 @@ import { serializeNameToken } from "../../../tokenscope/name-token"; import { type NameTokensResponse, NameTokensResponseCodes, + type NameTokensResponseError, + type NameTokensResponseOk, type RegisteredNameTokens, } from "./response"; import type { @@ -26,6 +28,10 @@ export function serializeRegisteredNameTokens({ }; } +export function serializeNameTokensResponse( + response: NameTokensResponseOk, +): SerializedNameTokensResponseOk; +export function serializeNameTokensResponse(response: T): T; export function serializeNameTokensResponse( response: NameTokensResponse, ): SerializedNameTokensResponse { diff --git a/packages/ensnode-sdk/src/ensapi/api/name-tokens/zod-schemas.test.ts b/packages/ensnode-sdk/src/ensapi/api/name-tokens/zod-schemas.test.ts index d6e82d6958..8db1ada4fc 100644 --- a/packages/ensnode-sdk/src/ensapi/api/name-tokens/zod-schemas.test.ts +++ b/packages/ensnode-sdk/src/ensapi/api/name-tokens/zod-schemas.test.ts @@ -3,10 +3,14 @@ import { describe, expect, it } from "vitest"; import { NFTMintStatuses } from "../../../tokenscope/assets"; import { NameTokenOwnershipTypes } from "../../../tokenscope/name-token"; -import { nameTokensResponseOkExample } from "./examples"; +import { + nameTokensNotIndexedExample, + nameTokensResponseOkExample, + nameTokensServiceUnavailableExample, +} from "./examples"; import { NameTokensResponseCodes, type NameTokensResponseOk } from "./response"; import type { SerializedNameTokensResponseOk } from "./serialized-response"; -import { makeNameTokensResponseSchema } from "./zod-schemas"; +import { makeNameTokensResponseErrorSchema, makeNameTokensResponseSchema } from "./zod-schemas"; const responseOk = { responseCode: NameTokensResponseCodes.Ok, @@ -57,6 +61,18 @@ const responseOk = { } satisfies SerializedNameTokensResponseOk; describe("Name Tokens: Zod Schemas", () => { + it("nameTokensServiceUnavailableExample passes error schema", () => { + expect( + makeNameTokensResponseErrorSchema().safeParse(nameTokensServiceUnavailableExample).success, + ).toBe(true); + }); + + it("nameTokensNotIndexedExample passes error schema", () => { + expect( + makeNameTokensResponseErrorSchema().safeParse(nameTokensNotIndexedExample).success, + ).toBe(true); + }); + it("nameTokensResponseOkExample passes schema", () => { expect( makeNameTokensResponseSchema("Name Tokens Response", true).safeParse( diff --git a/packages/ensnode-sdk/src/ensapi/api/shared/errors/examples.ts b/packages/ensnode-sdk/src/ensapi/api/shared/errors/examples.ts index 959b2d529d..52b4db4fc5 100644 --- a/packages/ensnode-sdk/src/ensapi/api/shared/errors/examples.ts +++ b/packages/ensnode-sdk/src/ensapi/api/shared/errors/examples.ts @@ -37,5 +37,6 @@ export const errorResponseInvalidAddressExample = { * Example value for {@link ErrorResponse} representing a 500 Internal Server Error, for use in OpenAPI documentation. */ export const errorResponseInternalServerErrorExample = { - message: "Internal server error", + message: "Internal Server Error", } satisfies ErrorResponse; + From eceb0097d356acc95310d3d9d694a41fc6cccd53 Mon Sep 17 00:00:00 2001 From: sevenzing Date: Tue, 14 Apr 2026 13:13:23 +0400 Subject: [PATCH 11/21] undo changes for app.onError --- .../src/handlers/api/resolution/resolution-api.routes.ts | 8 ++++++-- .../src/ensapi/api/name-tokens/zod-schemas.test.ts | 6 +++--- .../ensnode-sdk/src/ensapi/api/shared/errors/examples.ts | 1 - 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/apps/ensapi/src/handlers/api/resolution/resolution-api.routes.ts b/apps/ensapi/src/handlers/api/resolution/resolution-api.routes.ts index cacca68320..ea49de7b2b 100644 --- a/apps/ensapi/src/handlers/api/resolution/resolution-api.routes.ts +++ b/apps/ensapi/src/handlers/api/resolution/resolution-api.routes.ts @@ -94,7 +94,9 @@ export const resolvePrimaryNameRoute = createRoute({ description: "Invalid address or chain ID", content: { "application/json": { - schema: makeErrorResponseSchema().openapi({ example: errorResponseInvalidAddressExample }), + schema: makeErrorResponseSchema().openapi({ + example: errorResponseInvalidAddressExample, + }), }, }, }, @@ -143,7 +145,9 @@ export const resolvePrimaryNamesRoute = createRoute({ description: "Invalid address or chain IDs", content: { "application/json": { - schema: makeErrorResponseSchema().openapi({ example: errorResponseInvalidAddressExample }), + schema: makeErrorResponseSchema().openapi({ + example: errorResponseInvalidAddressExample, + }), }, }, }, diff --git a/packages/ensnode-sdk/src/ensapi/api/name-tokens/zod-schemas.test.ts b/packages/ensnode-sdk/src/ensapi/api/name-tokens/zod-schemas.test.ts index 8db1ada4fc..22c11fd00d 100644 --- a/packages/ensnode-sdk/src/ensapi/api/name-tokens/zod-schemas.test.ts +++ b/packages/ensnode-sdk/src/ensapi/api/name-tokens/zod-schemas.test.ts @@ -68,9 +68,9 @@ describe("Name Tokens: Zod Schemas", () => { }); it("nameTokensNotIndexedExample passes error schema", () => { - expect( - makeNameTokensResponseErrorSchema().safeParse(nameTokensNotIndexedExample).success, - ).toBe(true); + expect(makeNameTokensResponseErrorSchema().safeParse(nameTokensNotIndexedExample).success).toBe( + true, + ); }); it("nameTokensResponseOkExample passes schema", () => { diff --git a/packages/ensnode-sdk/src/ensapi/api/shared/errors/examples.ts b/packages/ensnode-sdk/src/ensapi/api/shared/errors/examples.ts index 52b4db4fc5..78b6546dc0 100644 --- a/packages/ensnode-sdk/src/ensapi/api/shared/errors/examples.ts +++ b/packages/ensnode-sdk/src/ensapi/api/shared/errors/examples.ts @@ -39,4 +39,3 @@ export const errorResponseInvalidAddressExample = { export const errorResponseInternalServerErrorExample = { message: "Internal Server Error", } satisfies ErrorResponse; - From 27026e8d538b3244a60be595dd8cb44d55ef3ae8 Mon Sep 17 00:00:00 2001 From: sevenzing Date: Tue, 14 Apr 2026 13:35:31 +0400 Subject: [PATCH 12/21] fix description --- apps/ensapi/src/lib/handlers/params.schema.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/apps/ensapi/src/lib/handlers/params.schema.ts b/apps/ensapi/src/lib/handlers/params.schema.ts index e37846c3f8..bc0eb01928 100644 --- a/apps/ensapi/src/lib/handlers/params.schema.ts +++ b/apps/ensapi/src/lib/handlers/params.schema.ts @@ -58,9 +58,7 @@ const coinType = makeCoinTypeStringSchema(); const chainIdsWithoutDefaultChainId = z .optional(stringarray.pipe(z.array(defaultableChainId.pipe(excludingDefaultChainId)))) - .describe( - "Comma-separated list of chain IDs to resolve primary names for (e.g. '1,10,8453'). The default EVM chain ID (0) is not allowed.", - ); + .describe("Comma-separated list of chain IDs to resolve primary names for (e.g. '1,10,8453')."); const rawSelectionParams = z.object({ reverseName: z From 958e450a5b541488486d4ffcea29dec49d082884 Mon Sep 17 00:00:00 2001 From: sevenzing Date: Tue, 14 Apr 2026 13:58:42 +0400 Subject: [PATCH 13/21] regenerate openapi doc --- docs/docs.ensnode.io/ensapi-openapi.json | 437 ++++------------------- 1 file changed, 64 insertions(+), 373 deletions(-) diff --git a/docs/docs.ensnode.io/ensapi-openapi.json b/docs/docs.ensnode.io/ensapi-openapi.json index c770aaf2f0..bc15d49bc1 100644 --- a/docs/docs.ensnode.io/ensapi-openapi.json +++ b/docs/docs.ensnode.io/ensapi-openapi.json @@ -1105,7 +1105,7 @@ "type": "object", "properties": { "message": { "type": "string" }, "details": {} }, "required": ["message"], - "example": { "message": "Internal server error" } + "example": { "message": "Internal Server Error" } } } } @@ -1215,7 +1215,7 @@ "type": "object", "properties": { "message": { "type": "string" }, "details": {} }, "required": ["message"], - "example": { "message": "Internal server error" } + "example": { "message": "Internal Server Error" } } } } @@ -1243,10 +1243,10 @@ { "schema": { "type": "string", - "description": "Comma-separated list of chain IDs to resolve primary names for (e.g. '1,10,8453'). The default EVM chain ID (0) is not allowed." + "description": "Comma-separated list of chain IDs to resolve primary names for (e.g. '1,10,8453')." }, "required": false, - "description": "Comma-separated list of chain IDs to resolve primary names for (e.g. '1,10,8453'). The default EVM chain ID (0) is not allowed.", + "description": "Comma-separated list of chain IDs to resolve primary names for (e.g. '1,10,8453').", "name": "chainIds", "in": "query" }, @@ -1335,7 +1335,7 @@ "type": "object", "properties": { "message": { "type": "string" }, "details": {} }, "required": ["message"], - "example": { "message": "Internal server error" } + "example": { "message": "Internal Server Error" } } } } @@ -1614,198 +1614,26 @@ "content": { "application/json": { "schema": { - "oneOf": [ - { + "type": "object", + "properties": { + "responseCode": { "type": "string", "enum": ["error"] }, + "errorCode": { "type": "string", "enum": ["name-tokens-not-indexed"] }, + "error": { "type": "object", - "properties": { - "responseCode": { "type": "string", "enum": ["ok"] }, - "registeredNameTokens": { - "type": "object", - "properties": { - "domainId": { "type": "string" }, - "name": { "type": "string" }, - "tokens": { - "type": "array", - "items": { - "type": "object", - "properties": { - "token": { - "type": "object", - "properties": { - "assetNamespace": { - "type": "string", - "enum": ["erc721", "erc1155"] - }, - "contract": { - "type": "object", - "properties": { - "chainId": { "type": "integer", "exclusiveMinimum": 0 }, - "address": { "type": "string" } - }, - "required": ["chainId", "address"], - "additionalProperties": false - }, - "tokenId": { "type": "string" } - }, - "required": ["assetNamespace", "contract", "tokenId"] - }, - "ownership": { - "oneOf": [ - { - "type": "object", - "properties": { - "ownershipType": { - "type": "string", - "enum": ["namewrapper"] - }, - "owner": { - "type": "object", - "properties": { - "chainId": { - "type": "integer", - "exclusiveMinimum": 0 - }, - "address": { "type": "string" } - }, - "required": ["chainId", "address"], - "additionalProperties": false - } - }, - "required": ["ownershipType", "owner"] - }, - { - "type": "object", - "properties": { - "ownershipType": { - "type": "string", - "enum": ["fully-onchain"] - }, - "owner": { - "type": "object", - "properties": { - "chainId": { - "type": "integer", - "exclusiveMinimum": 0 - }, - "address": { "type": "string" } - }, - "required": ["chainId", "address"], - "additionalProperties": false - } - }, - "required": ["ownershipType", "owner"] - }, - { - "type": "object", - "properties": { - "ownershipType": { "type": "string", "enum": ["burned"] }, - "owner": { - "type": "object", - "properties": { - "chainId": { - "type": "integer", - "exclusiveMinimum": 0 - }, - "address": { "type": "string" } - }, - "required": ["chainId", "address"], - "additionalProperties": false - } - }, - "required": ["ownershipType", "owner"] - }, - { - "type": "object", - "properties": { - "ownershipType": { - "type": "string", - "enum": ["unknown"] - }, - "owner": { - "type": "object", - "properties": { - "chainId": { - "type": "integer", - "exclusiveMinimum": 0 - }, - "address": { "type": "string" } - }, - "required": ["chainId", "address"], - "additionalProperties": false - } - }, - "required": ["ownershipType", "owner"] - } - ] - }, - "mintStatus": { "type": "string", "enum": ["minted", "burned"] } - }, - "required": ["token", "ownership", "mintStatus"] - }, - "minItems": 1 - }, - "expiresAt": { "type": "integer" }, - "accurateAsOf": { "type": "integer" } - }, - "required": ["domainId", "name", "tokens", "expiresAt", "accurateAsOf"] - } - }, - "required": ["responseCode", "registeredNameTokens"], - "additionalProperties": false - }, - { - "oneOf": [ - { - "type": "object", - "properties": { - "responseCode": { "type": "string", "enum": ["error"] }, - "errorCode": { "type": "string", "enum": ["name-tokens-not-indexed"] }, - "error": { - "type": "object", - "properties": { "message": { "type": "string" }, "details": {} }, - "required": ["message"] - } - }, - "required": ["responseCode", "errorCode", "error"], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "responseCode": { "type": "string", "enum": ["error"] }, - "errorCode": { - "type": "string", - "enum": ["unsupported-ensindexer-config"] - }, - "error": { - "type": "object", - "properties": { "message": { "type": "string" }, "details": {} }, - "required": ["message"] - } - }, - "required": ["responseCode", "errorCode", "error"], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "responseCode": { "type": "string", "enum": ["error"] }, - "errorCode": { - "type": "string", - "enum": ["unsupported-indexing-status"] - }, - "error": { - "type": "object", - "properties": { "message": { "type": "string" }, "details": {} }, - "required": ["message"] - } - }, - "required": ["responseCode", "errorCode", "error"], - "additionalProperties": false - } - ] + "properties": { "message": { "type": "string" }, "details": {} }, + "required": ["message"] + } + }, + "required": ["responseCode", "errorCode", "error"], + "additionalProperties": false, + "example": { + "responseCode": "error", + "errorCode": "name-tokens-not-indexed", + "error": { + "message": "No indexed Name Tokens found", + "details": "This ENSNode instance has not been configured to index tokens for the requested name: 'vitalik.eth'" } - ] + } } } } @@ -1818,7 +1646,7 @@ "type": "object", "properties": { "message": { "type": "string" }, "details": {} }, "required": ["message"], - "example": { "message": "Internal server error" } + "example": { "message": "Internal Server Error" } } } } @@ -1832,194 +1660,57 @@ { "type": "object", "properties": { - "responseCode": { "type": "string", "enum": ["ok"] }, - "registeredNameTokens": { + "responseCode": { "type": "string", "enum": ["error"] }, + "errorCode": { "type": "string", "enum": ["name-tokens-not-indexed"] }, + "error": { "type": "object", - "properties": { - "domainId": { "type": "string" }, - "name": { "type": "string" }, - "tokens": { - "type": "array", - "items": { - "type": "object", - "properties": { - "token": { - "type": "object", - "properties": { - "assetNamespace": { - "type": "string", - "enum": ["erc721", "erc1155"] - }, - "contract": { - "type": "object", - "properties": { - "chainId": { "type": "integer", "exclusiveMinimum": 0 }, - "address": { "type": "string" } - }, - "required": ["chainId", "address"], - "additionalProperties": false - }, - "tokenId": { "type": "string" } - }, - "required": ["assetNamespace", "contract", "tokenId"] - }, - "ownership": { - "oneOf": [ - { - "type": "object", - "properties": { - "ownershipType": { - "type": "string", - "enum": ["namewrapper"] - }, - "owner": { - "type": "object", - "properties": { - "chainId": { - "type": "integer", - "exclusiveMinimum": 0 - }, - "address": { "type": "string" } - }, - "required": ["chainId", "address"], - "additionalProperties": false - } - }, - "required": ["ownershipType", "owner"] - }, - { - "type": "object", - "properties": { - "ownershipType": { - "type": "string", - "enum": ["fully-onchain"] - }, - "owner": { - "type": "object", - "properties": { - "chainId": { - "type": "integer", - "exclusiveMinimum": 0 - }, - "address": { "type": "string" } - }, - "required": ["chainId", "address"], - "additionalProperties": false - } - }, - "required": ["ownershipType", "owner"] - }, - { - "type": "object", - "properties": { - "ownershipType": { "type": "string", "enum": ["burned"] }, - "owner": { - "type": "object", - "properties": { - "chainId": { - "type": "integer", - "exclusiveMinimum": 0 - }, - "address": { "type": "string" } - }, - "required": ["chainId", "address"], - "additionalProperties": false - } - }, - "required": ["ownershipType", "owner"] - }, - { - "type": "object", - "properties": { - "ownershipType": { - "type": "string", - "enum": ["unknown"] - }, - "owner": { - "type": "object", - "properties": { - "chainId": { - "type": "integer", - "exclusiveMinimum": 0 - }, - "address": { "type": "string" } - }, - "required": ["chainId", "address"], - "additionalProperties": false - } - }, - "required": ["ownershipType", "owner"] - } - ] - }, - "mintStatus": { "type": "string", "enum": ["minted", "burned"] } - }, - "required": ["token", "ownership", "mintStatus"] - }, - "minItems": 1 - }, - "expiresAt": { "type": "integer" }, - "accurateAsOf": { "type": "integer" } - }, - "required": ["domainId", "name", "tokens", "expiresAt", "accurateAsOf"] + "properties": { "message": { "type": "string" }, "details": {} }, + "required": ["message"] } }, - "required": ["responseCode", "registeredNameTokens"], + "required": ["responseCode", "errorCode", "error"], "additionalProperties": false }, { - "oneOf": [ - { - "type": "object", - "properties": { - "responseCode": { "type": "string", "enum": ["error"] }, - "errorCode": { "type": "string", "enum": ["name-tokens-not-indexed"] }, - "error": { - "type": "object", - "properties": { "message": { "type": "string" }, "details": {} }, - "required": ["message"] - } - }, - "required": ["responseCode", "errorCode", "error"], - "additionalProperties": false + "type": "object", + "properties": { + "responseCode": { "type": "string", "enum": ["error"] }, + "errorCode": { + "type": "string", + "enum": ["unsupported-ensindexer-config"] }, - { + "error": { "type": "object", - "properties": { - "responseCode": { "type": "string", "enum": ["error"] }, - "errorCode": { - "type": "string", - "enum": ["unsupported-ensindexer-config"] - }, - "error": { - "type": "object", - "properties": { "message": { "type": "string" }, "details": {} }, - "required": ["message"] - } - }, - "required": ["responseCode", "errorCode", "error"], - "additionalProperties": false - }, - { + "properties": { "message": { "type": "string" }, "details": {} }, + "required": ["message"] + } + }, + "required": ["responseCode", "errorCode", "error"], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "responseCode": { "type": "string", "enum": ["error"] }, + "errorCode": { "type": "string", "enum": ["unsupported-indexing-status"] }, + "error": { "type": "object", - "properties": { - "responseCode": { "type": "string", "enum": ["error"] }, - "errorCode": { - "type": "string", - "enum": ["unsupported-indexing-status"] - }, - "error": { - "type": "object", - "properties": { "message": { "type": "string" }, "details": {} }, - "required": ["message"] - } - }, - "required": ["responseCode", "errorCode", "error"], - "additionalProperties": false + "properties": { "message": { "type": "string" }, "details": {} }, + "required": ["message"] } - ] + }, + "required": ["responseCode", "errorCode", "error"], + "additionalProperties": false + } + ], + "example": { + "responseCode": "error", + "errorCode": "unsupported-ensindexer-config", + "error": { + "message": "Name Tokens API is not available", + "details": "Connected ENSIndexer must have all following plugins active: registrars, tokenscope" } - ] + } } } } From 78d1d08db37066f4d9aa09316158df7aea5d9b4f Mon Sep 17 00:00:00 2001 From: Tomek Kopacki Date: Mon, 20 Apr 2026 20:33:01 +0200 Subject: [PATCH 14/21] Fix type inference (#1957) --- .../explore/registrar-actions-api.routes.ts | 35 ++++---- docs/docs.ensnode.io/ensapi-openapi.json | 66 +++++++++++--- .../api/registrar-actions/zod-schemas.ts | 33 ++++++- .../ensnode-sdk/src/registrars/zod-schemas.ts | 87 +++++++++++++++---- .../ensnode-sdk/src/shared/zod-schemas.ts | 23 +++++ 5 files changed, 195 insertions(+), 49 deletions(-) diff --git a/apps/ensapi/src/handlers/api/explore/registrar-actions-api.routes.ts b/apps/ensapi/src/handlers/api/explore/registrar-actions-api.routes.ts index eb6a10a142..5b84bc2c51 100644 --- a/apps/ensapi/src/handlers/api/explore/registrar-actions-api.routes.ts +++ b/apps/ensapi/src/handlers/api/explore/registrar-actions-api.routes.ts @@ -11,7 +11,8 @@ import { makeLowercaseAddressSchema, makeNodeSchema, makePositiveIntegerSchema, - makeRegistrarActionsResponseOkSchema, + makeRegistrarActionsResponseErrorSchema, + makeSerializedRegistrarActionsResponseOkSchema, makeUnixTimestampSchema, registrarActionsResponseOkExample, } from "@ensnode/ensnode-sdk/internal"; @@ -104,7 +105,7 @@ export const getRegistrarActionsRoute = createRoute({ description: "Successfully retrieved registrar actions", content: { "application/json": { - schema: makeRegistrarActionsResponseOkSchema().openapi({ + schema: makeSerializedRegistrarActionsResponseOkSchema().openapi({ example: registrarActionsResponseOkExample, }), }, @@ -120,13 +121,11 @@ export const getRegistrarActionsRoute = createRoute({ }, 500: { description: "Internal server error", - // TODO: fix the problem with typechecking. - // Throws error ```Type '{ currency: "ETH"; amount: string; }' is not assignable to type 'null'.``` - // content: { - // "application/json": { - // schema: makeRegistrarActionsResponseErrorSchema("Registrar Actions Error Response"), - // }, - // }, + content: { + "application/json": { + schema: makeRegistrarActionsResponseErrorSchema("Registrar Actions Error Response"), + }, + }, }, }, }); @@ -152,7 +151,7 @@ export const getRegistrarActionsByParentNodeRoute = createRoute({ description: "Successfully retrieved registrar actions", content: { "application/json": { - schema: makeRegistrarActionsResponseOkSchema( + schema: makeSerializedRegistrarActionsResponseOkSchema( "Registrar Actions By ParentNode Response", ).openapi({ example: registrarActionsResponseOkExample, @@ -170,15 +169,13 @@ export const getRegistrarActionsByParentNodeRoute = createRoute({ }, 500: { description: "Internal server error", - // TODO: fix the problem with typechecking. - // Throws error ```Type '{ currency: "ETH"; amount: string; }' is not assignable to type 'null'.``` - // content: { - // "application/json": { - // schema: makeRegistrarActionsResponseErrorSchema( - // "Registrar Actions By ParentNode Error Response", - // ), - // }, - // }, + content: { + "application/json": { + schema: makeRegistrarActionsResponseErrorSchema( + "Registrar Actions By ParentNode Error Response", + ), + }, + }, }, }, }); diff --git a/docs/docs.ensnode.io/ensapi-openapi.json b/docs/docs.ensnode.io/ensapi-openapi.json index bc15d49bc1..df44d0af45 100644 --- a/docs/docs.ensnode.io/ensapi-openapi.json +++ b/docs/docs.ensnode.io/ensapi-openapi.json @@ -1858,7 +1858,7 @@ "baseCost": { "type": "object", "properties": { - "amount": { "type": "string", "pattern": "^d+$" }, + "amount": { "type": "string", "pattern": "^\\d+$" }, "currency": { "type": "string", "enum": ["ETH"] } }, "required": ["amount", "currency"], @@ -1867,7 +1867,7 @@ "premium": { "type": "object", "properties": { - "amount": { "type": "string", "pattern": "^d+$" }, + "amount": { "type": "string", "pattern": "^\\d+$" }, "currency": { "type": "string", "enum": ["ETH"] } }, "required": ["amount", "currency"], @@ -1876,7 +1876,7 @@ "total": { "type": "object", "properties": { - "amount": { "type": "string", "pattern": "^d+$" }, + "amount": { "type": "string", "pattern": "^\\d+$" }, "currency": { "type": "string", "enum": ["ETH"] } }, "required": ["amount", "currency"], @@ -1987,7 +1987,7 @@ "baseCost": { "type": "object", "properties": { - "amount": { "type": "string", "pattern": "^d+$" }, + "amount": { "type": "string", "pattern": "^\\d+$" }, "currency": { "type": "string", "enum": ["ETH"] } }, "required": ["amount", "currency"], @@ -1996,7 +1996,7 @@ "premium": { "type": "object", "properties": { - "amount": { "type": "string", "pattern": "^d+$" }, + "amount": { "type": "string", "pattern": "^\\d+$" }, "currency": { "type": "string", "enum": ["ETH"] } }, "required": ["amount", "currency"], @@ -2005,7 +2005,7 @@ "total": { "type": "object", "properties": { - "amount": { "type": "string", "pattern": "^d+$" }, + "amount": { "type": "string", "pattern": "^\\d+$" }, "currency": { "type": "string", "enum": ["ETH"] } }, "required": ["amount", "currency"], @@ -2203,7 +2203,26 @@ } } }, - "500": { "description": "Internal server error" } + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "responseCode": { "type": "string", "enum": ["error"] }, + "error": { + "type": "object", + "properties": { "message": { "type": "string" }, "details": {} }, + "required": ["message"] + } + }, + "required": ["responseCode", "error"], + "additionalProperties": false + } + } + } + } } } }, @@ -2357,7 +2376,7 @@ "baseCost": { "type": "object", "properties": { - "amount": { "type": "string", "pattern": "^d+$" }, + "amount": { "type": "string", "pattern": "^\\d+$" }, "currency": { "type": "string", "enum": ["ETH"] } }, "required": ["amount", "currency"], @@ -2366,7 +2385,7 @@ "premium": { "type": "object", "properties": { - "amount": { "type": "string", "pattern": "^d+$" }, + "amount": { "type": "string", "pattern": "^\\d+$" }, "currency": { "type": "string", "enum": ["ETH"] } }, "required": ["amount", "currency"], @@ -2375,7 +2394,7 @@ "total": { "type": "object", "properties": { - "amount": { "type": "string", "pattern": "^d+$" }, + "amount": { "type": "string", "pattern": "^\\d+$" }, "currency": { "type": "string", "enum": ["ETH"] } }, "required": ["amount", "currency"], @@ -2486,7 +2505,7 @@ "baseCost": { "type": "object", "properties": { - "amount": { "type": "string", "pattern": "^d+$" }, + "amount": { "type": "string", "pattern": "^\\d+$" }, "currency": { "type": "string", "enum": ["ETH"] } }, "required": ["amount", "currency"], @@ -2495,7 +2514,7 @@ "premium": { "type": "object", "properties": { - "amount": { "type": "string", "pattern": "^d+$" }, + "amount": { "type": "string", "pattern": "^\\d+$" }, "currency": { "type": "string", "enum": ["ETH"] } }, "required": ["amount", "currency"], @@ -2504,7 +2523,7 @@ "total": { "type": "object", "properties": { - "amount": { "type": "string", "pattern": "^d+$" }, + "amount": { "type": "string", "pattern": "^\\d+$" }, "currency": { "type": "string", "enum": ["ETH"] } }, "required": ["amount", "currency"], @@ -2702,7 +2721,26 @@ } } }, - "500": { "description": "Internal server error" } + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "responseCode": { "type": "string", "enum": ["error"] }, + "error": { + "type": "object", + "properties": { "message": { "type": "string" }, "details": {} }, + "required": ["message"] + } + }, + "required": ["responseCode", "error"], + "additionalProperties": false + } + } + } + } } } }, diff --git a/packages/ensnode-sdk/src/ensapi/api/registrar-actions/zod-schemas.ts b/packages/ensnode-sdk/src/ensapi/api/registrar-actions/zod-schemas.ts index b50c56cac3..c315ac45e0 100644 --- a/packages/ensnode-sdk/src/ensapi/api/registrar-actions/zod-schemas.ts +++ b/packages/ensnode-sdk/src/ensapi/api/registrar-actions/zod-schemas.ts @@ -2,11 +2,18 @@ import { namehashInterpretedName } from "enssdk"; import { z } from "zod/v4"; import type { ParsePayload } from "zod/v4/core"; -import { makeRegistrarActionSchema } from "../../../registrars/zod-schemas"; +import { + makeRegistrarActionSchema, + makeSerializedRegistrarActionSchema, +} from "../../../registrars/zod-schemas"; import { makeReinterpretedNameSchema, makeUnixTimestampSchema } from "../../../shared/zod-schemas"; import { makeErrorResponseSchema } from "../shared/errors/zod-schemas"; import { makeResponsePageContextSchema } from "../shared/pagination/zod-schemas"; import { type NamedRegistrarAction, RegistrarActionsResponseCodes } from "./response"; +import { + SerializedNamedRegistrarAction, + SerializedRegistrarActionsResponseOk, +} from "./serialized-response"; function invariant_registrationLifecycleNodeMatchesName(ctx: ParsePayload) { const { name, action } = ctx.value; @@ -33,6 +40,17 @@ export const makeNamedRegistrarActionSchema = (valueLabel: string = "Named Regis }) .check(invariant_registrationLifecycleNodeMatchesName); +/** + * Schema for {@link SerializedNamedRegistrarAction}. + */ +const makeSerializedNamedRegistrarActionSchema = ( + valueLabel: string = "Serialized Named Registrar Action", +) => + z.object({ + action: makeSerializedRegistrarActionSchema(valueLabel), + name: makeReinterpretedNameSchema(valueLabel), + }); + /** * Schema for {@link RegistrarActionsResponseOk} */ @@ -67,3 +85,16 @@ export const makeRegistrarActionsResponseSchema = ( makeRegistrarActionsResponseOkSchema(valueLabel), makeRegistrarActionsResponseErrorSchema(valueLabel), ]); + +/** + * Schema for {@link SerializedRegistrarActionsResponseOk} + */ +export const makeSerializedRegistrarActionsResponseOkSchema = ( + valueLabel: string = "Serialized Registrar Actions Response OK", +) => + z.object({ + responseCode: z.literal(RegistrarActionsResponseCodes.Ok), + registrarActions: z.array(makeSerializedNamedRegistrarActionSchema(valueLabel)), + pageContext: makeResponsePageContextSchema(`${valueLabel}.pageContext`), + accurateAsOf: makeUnixTimestampSchema(`${valueLabel}.accurateAsOf`), + }); diff --git a/packages/ensnode-sdk/src/registrars/zod-schemas.ts b/packages/ensnode-sdk/src/registrars/zod-schemas.ts index ff6cca19a2..a8e72f4466 100644 --- a/packages/ensnode-sdk/src/registrars/zod-schemas.ts +++ b/packages/ensnode-sdk/src/registrars/zod-schemas.ts @@ -10,6 +10,7 @@ import { makeNodeSchema, makeNormalizedAddressSchema, makePriceEthSchema, + makeSerializedPriceEthSchema, makeTransactionHashSchema, makeUnixTimestampSchema, } from "../shared/zod-schemas"; @@ -22,6 +23,8 @@ import { type RegistrarActionPricingUnknown, type RegistrarActionReferralAvailable, RegistrarActionTypes, + SerializedRegistrarAction, + SerializedRegistrarActionPricing, } from "./registrar-action"; import type { RegistrationLifecycle } from "./registration-lifecycle"; import { Subregistry } from "./subregistry"; @@ -86,6 +89,27 @@ const makeRegistrarActionPricingSchema = (valueLabel: string = "Registrar Action .transform((v) => v as RegistrarActionPricingUnknown), ]); +/** + * Schema for parsing objects into {@link SerializedRegistrarActionPricing}. + */ +export const makeSerializedRegistrarActionPricingSchema = ( + valueLabel: string = "Serialized Registrar Action Pricing", +) => + z.union([ + // pricing available + z.object({ + baseCost: makeSerializedPriceEthSchema(`${valueLabel} Base Cost`), + premium: makeSerializedPriceEthSchema(`${valueLabel} Premium`), + total: makeSerializedPriceEthSchema(`${valueLabel} Total`), + }), + // pricing unknown + z.object({ + baseCost: z.null(), + premium: z.null(), + total: z.null(), + }), + ]); + /** Invariant: decodedReferrer is based on encodedReferrer */ function invariant_registrarActionDecodedReferrerBasedOnRawReferrer( ctx: ParsePayload, @@ -156,22 +180,25 @@ const EventIdsSchema = z .min(1) .transform((v) => v as [RegistrarActionEventId, ...RegistrarActionEventId[]]); +// Base schema without refinements - can be extended +const makeBaseRegistrarActionSchemaWithoutCheck = (valueLabel: string = "Base Registrar Action") => + z.object({ + id: EventIdSchema, + incrementalDuration: makeDurationSchema(`${valueLabel} Incremental Duration`), + registrant: makeLowercaseAddressSchema(`${valueLabel} Registrant`), + registrationLifecycle: makeRegistrationLifecycleSchema(`${valueLabel} Registration Lifecycle`), + pricing: makeRegistrarActionPricingSchema(`${valueLabel} Pricing`), + referral: makeRegistrarActionReferralSchema(`${valueLabel} Referral`), + block: makeBlockRefSchema(`${valueLabel} Block`), + transactionHash: makeTransactionHashSchema(`${valueLabel} Transaction Hash`), + eventIds: EventIdsSchema, + }); + +// Base schema with refinements - used for parsing/validation export const makeBaseRegistrarActionSchema = (valueLabel: string = "Base Registrar Action") => - z - .object({ - id: EventIdSchema, - incrementalDuration: makeDurationSchema(`${valueLabel} Incremental Duration`), - registrant: makeNormalizedAddressSchema(`${valueLabel} Registrant`), - registrationLifecycle: makeRegistrationLifecycleSchema( - `${valueLabel} Registration Lifecycle`, - ), - pricing: makeRegistrarActionPricingSchema(`${valueLabel} Pricing`), - referral: makeRegistrarActionReferralSchema(`${valueLabel} Referral`), - block: makeBlockRefSchema(`${valueLabel} Block`), - transactionHash: makeTransactionHashSchema(`${valueLabel} Transaction Hash`), - eventIds: EventIdsSchema, - }) - .check(invariant_eventIdsInitialElementIsTheActionId); + makeBaseRegistrarActionSchemaWithoutCheck(valueLabel).check( + invariant_eventIdsInitialElementIsTheActionId, + ); export const makeRegistrarActionRegistrationSchema = (valueLabel: string = "Registration ") => makeBaseRegistrarActionSchema(valueLabel).extend({ @@ -191,3 +218,33 @@ export const makeRegistrarActionSchema = (valueLabel: string = "Registrar Action makeRegistrarActionRegistrationSchema(`${valueLabel} Registration`), makeRegistrarActionRenewalSchema(`${valueLabel} Renewal`), ]); + +const makeSerializedBaseRegistrarActionSchema = ( + valueLabel: string = "Serialized Base Registrar Action", +) => + makeBaseRegistrarActionSchemaWithoutCheck(valueLabel).extend({ + pricing: makeSerializedRegistrarActionPricingSchema(`${valueLabel} Pricing`), + }); + +const makeSerializedRegistrarActionRegistrationSchema = ( + valueLabel: string = "Serialized Registration", +) => + makeSerializedBaseRegistrarActionSchema(valueLabel).extend({ + type: z.literal(RegistrarActionTypes.Registration), + }); + +const makeSerializedRegistrarActionRenewalSchema = (valueLabel: string = "Serialized Renewal") => + makeSerializedBaseRegistrarActionSchema(valueLabel).extend({ + type: z.literal(RegistrarActionTypes.Renewal), + }); + +/** + * Schema for {@link SerializedRegistrarAction} + */ +export const makeSerializedRegistrarActionSchema = ( + valueLabel: string = "Serialized Registrar Action", +) => + z.discriminatedUnion("type", [ + makeSerializedRegistrarActionRegistrationSchema(`${valueLabel} Registration`), + makeSerializedRegistrarActionRenewalSchema(`${valueLabel} Renewal`), + ]); diff --git a/packages/ensnode-sdk/src/shared/zod-schemas.ts b/packages/ensnode-sdk/src/shared/zod-schemas.ts index 92746e9618..d9a352f591 100644 --- a/packages/ensnode-sdk/src/shared/zod-schemas.ts +++ b/packages/ensnode-sdk/src/shared/zod-schemas.ts @@ -29,6 +29,7 @@ import { type PriceDai, type PriceEth, type PriceUsdc, + type SerializedPriceEth, } from "./currencies"; import type { BlockRef, Datetime } from "./types"; @@ -242,6 +243,11 @@ const makePriceAmountSchema = (valueLabel: string = "Amount") => error: `${valueLabel} must not be negative.`, }); +const makeSerializedCurrencyAmountSchema = (valueLabel: string = "Serialized Currency Amount") => + z.string({ error: `${valueLabel} must be a string.` }).regex(/^\d+$/, { + error: `${valueLabel} can only contain digits (0-9) and must represent a non-negative integer.`, + }); + export const makePriceCurrencySchema = ( currency: CurrencyId, valueLabel: string = "Price Currency", @@ -254,6 +260,18 @@ export const makePriceCurrencySchema = ( }), }); +export const makeSerializedPriceCurrencySchema = ( + currency: CurrencyId, + valueLabel: string = "Price Currency", +) => + z.strictObject({ + amount: makeSerializedCurrencyAmountSchema(`${valueLabel} amount`), + + currency: z.literal(currency, { + error: `${valueLabel} currency must be set to '${currency}'.`, + }), + }); + /** * Schema for {@link Price} type. */ @@ -274,6 +292,11 @@ export const makePriceSchema = (valueLabel: string = "Price") => export const makePriceEthSchema = (valueLabel: string = "Price ETH") => makePriceCurrencySchema(CurrencyIds.ETH, valueLabel).transform((v) => v as PriceEth); +export const makeSerializedPriceEthSchema = (valueLabel: string = "Serialized Price ETH") => + makeSerializedPriceCurrencySchema(CurrencyIds.ETH, valueLabel).transform( + (v) => v as SerializedPriceEth, + ); + /** * Schema for {@link PriceUsdc} type. */ From 1c381d3a0eab5626dfaf22d297b4ae95f56d35c5 Mon Sep 17 00:00:00 2001 From: sevenzing Date: Mon, 20 Apr 2026 23:17:04 +0400 Subject: [PATCH 15/21] fix invalid merge conflict --- .../explore/registrar-actions-api.routes.ts | 4 +- apps/ensapi/src/lib/handlers/params.schema.ts | 28 ++++- docs/docs.ensnode.io/ensapi-openapi.json | 101 ++---------------- .../ensnode-sdk/src/registrars/zod-schemas.ts | 2 +- 4 files changed, 39 insertions(+), 96 deletions(-) diff --git a/apps/ensapi/src/handlers/api/explore/registrar-actions-api.routes.ts b/apps/ensapi/src/handlers/api/explore/registrar-actions-api.routes.ts index 5b84bc2c51..712fbb5017 100644 --- a/apps/ensapi/src/handlers/api/explore/registrar-actions-api.routes.ts +++ b/apps/ensapi/src/handlers/api/explore/registrar-actions-api.routes.ts @@ -8,8 +8,8 @@ import { import { errorResponseBadRequestExample, makeErrorResponseSchema, - makeLowercaseAddressSchema, makeNodeSchema, + makeNormalizedAddressSchema, makePositiveIntegerSchema, makeRegistrarActionsResponseErrorSchema, makeSerializedRegistrarActionsResponseOkSchema, @@ -56,7 +56,7 @@ export const registrarActionsQuerySchema = z .describe("Filter to only include actions with referrals") .openapi({ default: false }), - decodedReferrer: makeLowercaseAddressSchema("decodedReferrer") + decodedReferrer: makeNormalizedAddressSchema("decodedReferrer") .optional() .describe("Filter by decoded referrer address"), diff --git a/apps/ensapi/src/lib/handlers/params.schema.ts b/apps/ensapi/src/lib/handlers/params.schema.ts index bc0eb01928..6b045c136a 100644 --- a/apps/ensapi/src/lib/handlers/params.schema.ts +++ b/apps/ensapi/src/lib/handlers/params.schema.ts @@ -5,7 +5,7 @@ import { isSelectionEmpty, type ResolverRecordsSelection } from "@ensnode/ensnod import { makeCoinTypeStringSchema, makeDefaultableChainIdStringSchema, - makeLowercaseAddressSchema, + makeNormalizedAddressSchema, } from "@ensnode/ensnode-sdk/internal"; const excludingDefaultChainId = z @@ -48,7 +48,7 @@ const accelerate = z .openapi({ default: false, }); -const address = makeLowercaseAddressSchema().describe( +const address = makeNormalizedAddressSchema().describe( "EVM wallet address (e.g. '0xd8da6bf26964af9d7eed9e03e53415d37aa96045').", ); const defaultableChainId = makeDefaultableChainIdStringSchema().describe( @@ -130,6 +130,29 @@ const selection = z */ const queryParam = z.preprocess((v) => (v === "" ? undefined : v), z.unknown()); +const resolveRecordsQuery = z + .object({ + reverseName: z.optional(boolstring), + addresses: z.optional(stringarray.pipe(z.array(coinType))), + texts: z.optional(stringarray), + trace, + accelerate, + }) + .transform(({ trace, accelerate, ...selectionFields }, ctx) => { + const sel: ResolverRecordsSelection = { + ...(selectionFields.reverseName && { name: true }), + ...(selectionFields.addresses && { addresses: selectionFields.addresses }), + ...(selectionFields.texts && { texts: selectionFields.texts }), + }; + + if (isSelectionEmpty(sel)) { + ctx.issues.push({ code: "custom", message: "Selection cannot be empty.", input: sel }); + return z.NEVER; + } + + return { selection: sel, trace, accelerate }; + }); + export const params = { boolstring, stringarray, @@ -141,6 +164,7 @@ export const params = { coinType, selectionParams: rawSelectionParams, selection, + resolveRecordsQuery, chainIdsWithoutDefaultChainId, queryParam, }; diff --git a/docs/docs.ensnode.io/ensapi-openapi.json b/docs/docs.ensnode.io/ensapi-openapi.json index df44d0af45..803fb9db24 100644 --- a/docs/docs.ensnode.io/ensapi-openapi.json +++ b/docs/docs.ensnode.io/ensapi-openapi.json @@ -980,36 +980,13 @@ "in": "path" }, { - "schema": { - "type": "string", - "description": "Whether to include the reverse name record in the response, see ENSIP-19 (https://docs.ens.domains/ensip/19/#reverse-resolution)", - "enum": ["true", "false"] - }, + "schema": { "type": "boolean" }, "required": false, - "description": "Whether to include the reverse name record in the response, see ENSIP-19 (https://docs.ens.domains/ensip/19/#reverse-resolution)", "name": "reverseName", "in": "query" }, - { - "schema": { - "type": "string", - "description": "Comma-separated list of coin types to resolve addresses for (e.g. '60' for ETH, '2147483658' for OP)." - }, - "required": false, - "description": "Comma-separated list of coin types to resolve addresses for (e.g. '60' for ETH, '2147483658' for OP).", - "name": "addresses", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "Comma-separated list of text record keys to resolve (e.g. 'avatar,description,url')." - }, - "required": false, - "description": "Comma-separated list of text record keys to resolve (e.g. 'avatar,description,url').", - "name": "texts", - "in": "query" - }, + { "schema": { "type": "string" }, "required": false, "name": "addresses", "in": "query" }, + { "schema": { "type": "string" }, "required": false, "name": "texts", "in": "query" }, { "schema": { "type": "boolean", @@ -2744,70 +2721,11 @@ } } }, - "/ensanalytics/referrers": { - "get": { - "operationId": "getReferrerLeaderboard", - "tags": ["ENSAwards"], - "summary": "Get Referrer Leaderboard", - "description": "Returns a paginated page from the referrer leaderboard", - "parameters": [ - { - "schema": { - "type": "integer", - "minimum": 1, - "description": "Page number for pagination" - }, - "required": false, - "description": "Page number for pagination", - "name": "page", - "in": "query" - }, - { - "schema": { - "type": "integer", - "minimum": 1, - "maximum": 100, - "description": "Number of referrers per page" - }, - "required": false, - "description": "Number of referrers per page", - "name": "recordsPerPage", - "in": "query" - } - ], - "responses": { - "200": { "description": "Successfully retrieved referrer leaderboard page" }, - "500": { "description": "Internal server error" } - } - } - }, - "/ensanalytics/referrers/{referrer}": { - "get": { - "operationId": "getReferrerDetail", - "tags": ["ENSAwards"], - "summary": "Get Referrer Detail", - "description": "Returns detailed information for a specific referrer by address", - "parameters": [ - { - "schema": { "type": "string", "description": "Referrer Ethereum address" }, - "required": true, - "description": "Referrer Ethereum address", - "name": "referrer", - "in": "path" - } - ], - "responses": { - "200": { "description": "Successfully retrieved referrer detail" }, - "500": { "description": "Internal server error" }, - "503": { "description": "Service unavailable - referrer leaderboard data not yet cached" } - } - } - }, "/v1/ensanalytics/referral-leaderboard": { "get": { - "operationId": "getReferralLeaderboard_v1", + "operationId": "getReferralLeaderboard", "tags": ["ENSAwards"], - "summary": "Get Referrer Leaderboard (v1)", + "summary": "Get Referrer Leaderboard", "description": "Returns a paginated page from the referrer leaderboard for a specific edition", "parameters": [ { @@ -2842,6 +2760,7 @@ ], "responses": { "200": { "description": "Successfully retrieved referrer leaderboard page" }, + "400": { "description": "Invalid request" }, "404": { "description": "Unknown edition slug" }, "500": { "description": "Internal server error" }, "503": { "description": "Service unavailable" } @@ -2850,9 +2769,9 @@ }, "/v1/ensanalytics/referrer/{referrer}": { "get": { - "operationId": "getReferrerDetail_v1", + "operationId": "getReferrerDetail", "tags": ["ENSAwards"], - "summary": "Get Referrer Detail for Editions (v1)", + "summary": "Get Referrer Detail for Editions", "description": "Returns detailed information for a specific referrer for the requested editions. Requires 1-20 distinct edition slugs. All requested editions must be recognized and have cached data, or the request fails.", "parameters": [ { @@ -2881,9 +2800,9 @@ }, "/v1/ensanalytics/editions": { "get": { - "operationId": "getEditions_v1", + "operationId": "getEditions", "tags": ["ENSAwards"], - "summary": "Get Edition Summaries (v1)", + "summary": "Get Edition Summaries", "description": "Returns a summary for each configured referral program edition, including its current status and award-model-specific runtime data. Editions are sorted in descending order by start timestamp (most recent first).", "responses": { "200": { "description": "Successfully retrieved edition summaries." }, diff --git a/packages/ensnode-sdk/src/registrars/zod-schemas.ts b/packages/ensnode-sdk/src/registrars/zod-schemas.ts index a8e72f4466..319a6d2b3d 100644 --- a/packages/ensnode-sdk/src/registrars/zod-schemas.ts +++ b/packages/ensnode-sdk/src/registrars/zod-schemas.ts @@ -185,7 +185,7 @@ const makeBaseRegistrarActionSchemaWithoutCheck = (valueLabel: string = "Base Re z.object({ id: EventIdSchema, incrementalDuration: makeDurationSchema(`${valueLabel} Incremental Duration`), - registrant: makeLowercaseAddressSchema(`${valueLabel} Registrant`), + registrant: makeNormalizedAddressSchema(`${valueLabel} Registrant`), registrationLifecycle: makeRegistrationLifecycleSchema(`${valueLabel} Registration Lifecycle`), pricing: makeRegistrarActionPricingSchema(`${valueLabel} Pricing`), referral: makeRegistrarActionReferralSchema(`${valueLabel} Referral`), From acf0a89c53b35a9bd8caf458a70c124208f04f34 Mon Sep 17 00:00:00 2001 From: sevenzing Date: Mon, 20 Apr 2026 23:25:13 +0400 Subject: [PATCH 16/21] final fixes --- .../src/lib/handlers/params.schema.test.ts | 2 +- apps/ensapi/src/lib/handlers/params.schema.ts | 83 ++++++++----------- docs/docs.ensnode.io/ensapi-openapi.json | 7 +- 3 files changed, 37 insertions(+), 55 deletions(-) diff --git a/apps/ensapi/src/lib/handlers/params.schema.test.ts b/apps/ensapi/src/lib/handlers/params.schema.test.ts index a7d3411fb4..496c4e0480 100644 --- a/apps/ensapi/src/lib/handlers/params.schema.test.ts +++ b/apps/ensapi/src/lib/handlers/params.schema.test.ts @@ -27,7 +27,7 @@ describe("params.selection", () => { it("parses selection", () => { expect( params.selection.parse({ - reverseName: "true", + name: "true", addresses: "60,0", texts: "example,hello", }), diff --git a/apps/ensapi/src/lib/handlers/params.schema.ts b/apps/ensapi/src/lib/handlers/params.schema.ts index 6b045c136a..b033c9edb8 100644 --- a/apps/ensapi/src/lib/handlers/params.schema.ts +++ b/apps/ensapi/src/lib/handlers/params.schema.ts @@ -61,7 +61,7 @@ const chainIdsWithoutDefaultChainId = z .describe("Comma-separated list of chain IDs to resolve primary names for (e.g. '1,10,8453')."); const rawSelectionParams = z.object({ - reverseName: z + name: z .string() .optional() .describe( @@ -84,30 +84,40 @@ const rawSelectionParams = z.object({ ), }); -const selection = z - .object({ - reverseName: z.optional(boolstring), - addresses: z.optional(stringarray.pipe(z.array(coinType))), - texts: z.optional(stringarray), - }) - .transform((value, ctx) => { - const selection: ResolverRecordsSelection = { - ...(value.reverseName && { name: true }), - ...(value.addresses && { addresses: value.addresses }), - ...(value.texts && { texts: value.texts }), - }; - - if (isSelectionEmpty(selection)) { - ctx.issues.push({ - code: "custom", - message: "Selection cannot be empty.", - input: selection, - }); - - return z.NEVER; - } - - return selection; +const selectionFields = z.object({ + name: z.optional(boolstring), + addresses: z.optional(stringarray.pipe(z.array(coinType))), + texts: z.optional(stringarray), +}); + +type SelectionFields = z.output; + +function toSelection( + fields: SelectionFields, + ctx: z.RefinementCtx, +): ResolverRecordsSelection | typeof z.NEVER { + const sel: ResolverRecordsSelection = { + ...(fields.name && { name: true }), + ...(fields.addresses && { addresses: fields.addresses }), + ...(fields.texts && { texts: fields.texts }), + }; + + if (isSelectionEmpty(sel)) { + ctx.issues.push({ code: "custom", message: "Selection cannot be empty.", input: sel }); + return z.NEVER; + } + + return sel; +} + +const selection = selectionFields.transform(toSelection); + +const resolveRecordsQuery = z + .object({ ...selectionFields.shape, trace, accelerate }) + .transform(({ trace, accelerate, ...fields }, ctx) => { + const sel = toSelection(fields, ctx); + if (sel === z.NEVER) return z.NEVER; + return { selection: sel, trace, accelerate }; }); /** @@ -130,29 +140,6 @@ const selection = z */ const queryParam = z.preprocess((v) => (v === "" ? undefined : v), z.unknown()); -const resolveRecordsQuery = z - .object({ - reverseName: z.optional(boolstring), - addresses: z.optional(stringarray.pipe(z.array(coinType))), - texts: z.optional(stringarray), - trace, - accelerate, - }) - .transform(({ trace, accelerate, ...selectionFields }, ctx) => { - const sel: ResolverRecordsSelection = { - ...(selectionFields.reverseName && { name: true }), - ...(selectionFields.addresses && { addresses: selectionFields.addresses }), - ...(selectionFields.texts && { texts: selectionFields.texts }), - }; - - if (isSelectionEmpty(sel)) { - ctx.issues.push({ code: "custom", message: "Selection cannot be empty.", input: sel }); - return z.NEVER; - } - - return { selection: sel, trace, accelerate }; - }); - export const params = { boolstring, stringarray, diff --git a/docs/docs.ensnode.io/ensapi-openapi.json b/docs/docs.ensnode.io/ensapi-openapi.json index 803fb9db24..95868843ef 100644 --- a/docs/docs.ensnode.io/ensapi-openapi.json +++ b/docs/docs.ensnode.io/ensapi-openapi.json @@ -979,12 +979,7 @@ "name": "name", "in": "path" }, - { - "schema": { "type": "boolean" }, - "required": false, - "name": "reverseName", - "in": "query" - }, + { "schema": { "type": "boolean" }, "required": false, "name": "name", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "addresses", "in": "query" }, { "schema": { "type": "string" }, "required": false, "name": "texts", "in": "query" }, { From d3f78537ca8d15873945913cebc29f4020cd5651 Mon Sep 17 00:00:00 2001 From: sevenzing Date: Mon, 20 Apr 2026 23:31:44 +0400 Subject: [PATCH 17/21] adding tests for serialized schema --- .../api/registrar-actions/zod-schemas.test.ts | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/packages/ensnode-sdk/src/ensapi/api/registrar-actions/zod-schemas.test.ts b/packages/ensnode-sdk/src/ensapi/api/registrar-actions/zod-schemas.test.ts index 5d3a3c9995..ec6a5615b2 100644 --- a/packages/ensnode-sdk/src/ensapi/api/registrar-actions/zod-schemas.test.ts +++ b/packages/ensnode-sdk/src/ensapi/api/registrar-actions/zod-schemas.test.ts @@ -8,7 +8,10 @@ import type { SerializedRegistrarActionsResponseError, SerializedRegistrarActionsResponseOk, } from "./serialized-response"; -import { makeRegistrarActionsResponseSchema } from "./zod-schemas"; +import { + makeRegistrarActionsResponseSchema, + makeSerializedRegistrarActionsResponseOkSchema, +} from "./zod-schemas"; describe("ENSNode API Schema", () => { describe("Registrar Actions API", () => { @@ -154,5 +157,24 @@ describe("ENSNode API Schema", () => { error: validResponseError.error, } satisfies RegistrarActionsResponseError); }); + + describe("makeSerializedRegistrarActionsResponseOkSchema", () => { + it("registrarActionsResponseOkExample passes schema", () => { + expect( + makeSerializedRegistrarActionsResponseOkSchema().safeParse( + registrarActionsResponseOkExample, + ).success, + ).toBe(true); + }); + + it("rejects ResponseOk object missing required accurateAsOf", () => { + const { accurateAsOf: _accurateAsOf, ...invalidResponseOk } = + registrarActionsResponseOkExample; + + expect( + makeSerializedRegistrarActionsResponseOkSchema().safeParse(invalidResponseOk).success, + ).toBe(false); + }); + }); }); }); From c38f18606a0f5b061bc1c9ee8a2503368a0820ea Mon Sep 17 00:00:00 2001 From: sevenzing Date: Mon, 20 Apr 2026 23:49:07 +0400 Subject: [PATCH 18/21] more tests for error responses --- .../ensapi/api/shared/errors/zod-schemas.test.ts | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/packages/ensnode-sdk/src/ensapi/api/shared/errors/zod-schemas.test.ts b/packages/ensnode-sdk/src/ensapi/api/shared/errors/zod-schemas.test.ts index 703bf6b3b3..3a68f9e3eb 100644 --- a/packages/ensnode-sdk/src/ensapi/api/shared/errors/zod-schemas.test.ts +++ b/packages/ensnode-sdk/src/ensapi/api/shared/errors/zod-schemas.test.ts @@ -1,10 +1,20 @@ import { describe, expect, it } from "vitest"; -import { errorResponseBadRequestExample } from "./examples"; +import { + errorResponseBadRequestExample, + errorResponseInternalServerErrorExample, + errorResponseInvalidAddressExample, + errorResponseInvalidNameExample, +} from "./examples"; import { makeErrorResponseSchema } from "./zod-schemas"; describe("makeErrorResponseSchema", () => { - it("errorResponseBadRequestExample passes schema", () => { - expect(makeErrorResponseSchema().safeParse(errorResponseBadRequestExample).success).toBe(true); + it.each([ + ["errorResponseBadRequestExample", errorResponseBadRequestExample], + ["errorResponseInvalidNameExample", errorResponseInvalidNameExample], + ["errorResponseInvalidAddressExample", errorResponseInvalidAddressExample], + ["errorResponseInternalServerErrorExample", errorResponseInternalServerErrorExample], + ])("%s passes schema", (_name, example) => { + expect(makeErrorResponseSchema().safeParse(example).success).toBe(true); }); }); From 1c654b86af5cf80cd78162a3e4f04da60a0ca03b Mon Sep 17 00:00:00 2001 From: sevenzing Date: Mon, 20 Apr 2026 23:56:59 +0400 Subject: [PATCH 19/21] add satisfies to examples --- .../src/ensapi/api/resolution/examples.ts | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/packages/ensnode-sdk/src/ensapi/api/resolution/examples.ts b/packages/ensnode-sdk/src/ensapi/api/resolution/examples.ts index 3d0cd74b50..8d8aaf802c 100644 --- a/packages/ensnode-sdk/src/ensapi/api/resolution/examples.ts +++ b/packages/ensnode-sdk/src/ensapi/api/resolution/examples.ts @@ -1,3 +1,10 @@ +import type { ResolverRecordsSelection } from "../../../resolution"; +import type { + ResolvePrimaryNameResponse, + ResolvePrimaryNamesResponse, + ResolveRecordsResponse, +} from "./types"; + /** * Example values for {@link ResolveRecordsResponse}, for use in OpenAPI documentation. */ @@ -10,7 +17,7 @@ export const resolveRecordsResponseExample = { }, accelerationRequested: false, accelerationAttempted: false, -}; +} satisfies ResolveRecordsResponse; /** * Example values for {@link ResolvePrimaryNameResponse}, for use in OpenAPI documentation. @@ -19,7 +26,7 @@ export const resolvePrimaryNameResponseExample = { name: "jesse.base.eth", accelerationRequested: false, accelerationAttempted: false, -}; +} satisfies ResolvePrimaryNameResponse; /** * Example values for {@link ResolvePrimaryNamesResponse}, for use in OpenAPI documentation. @@ -35,4 +42,4 @@ export const resolvePrimaryNamesResponseExample = { }, accelerationRequested: false, accelerationAttempted: false, -}; +} satisfies ResolvePrimaryNamesResponse; From 1646bde6f18011a4e4b6a36b7a0f7a96a4ed6dbf Mon Sep 17 00:00:00 2001 From: sevenzing Date: Tue, 21 Apr 2026 21:44:42 +0400 Subject: [PATCH 20/21] more describe comments --- apps/ensapi/src/lib/handlers/params.schema.ts | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/apps/ensapi/src/lib/handlers/params.schema.ts b/apps/ensapi/src/lib/handlers/params.schema.ts index b033c9edb8..a911128cc0 100644 --- a/apps/ensapi/src/lib/handlers/params.schema.ts +++ b/apps/ensapi/src/lib/handlers/params.schema.ts @@ -38,7 +38,9 @@ const name = z const trace = z .optional(boolstring) .default(false) - .describe("Include detailed resolution trace information in the response.") + .describe( + "Include detailed OpenTelemetry trace information about the resolution in the response.", + ) .openapi({ default: false }); const accelerate = z @@ -60,13 +62,23 @@ const chainIdsWithoutDefaultChainId = z .optional(stringarray.pipe(z.array(defaultableChainId.pipe(excludingDefaultChainId)))) .describe("Comma-separated list of chain IDs to resolve primary names for (e.g. '1,10,8453')."); +const nameParamDescription = + "Whether to include the reverse name record in the response," + + "see ENSIP-3 (https://docs.ens.domains/ensip/3#resolver-interface). " + + "Resolving the reverse 'name' resolver record is relevant as an internal implementation detail of the ENS reverse resolution process " + + "where the primary ENS name associated with an address is being determined through a multi-step resolution process. " + + "For example, querying the (unvalidated!) reverse 'name' resolver record for Vitalik's address (0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045) " + + "is achieved by resolving the reverse 'name' resolver record of the reverse name 'd8da6bf26964af9d7eed9e03e53415d37aa96045.addr.reverse' " + + "which (at the time of writing) returns 'vitalik.eth'. " + + "The ENS reverse resolution process requires that any reverse 'name' resolver record must also pass a forward-resolution validation " + + "to be represented as the primary name for an address. " + + "More details here: https://docs.ens.domains/web/reverse"; + const rawSelectionParams = z.object({ name: z .string() .optional() - .describe( - "Whether to include the reverse name record in the response, see ENSIP-19 (https://docs.ens.domains/ensip/19/#reverse-resolution)", - ) + .describe(nameParamDescription) .openapi({ enum: ["true", "false"], }), From 9bbf5cfb1dab0bab09d9aa8b75c15607ffe1c88d Mon Sep 17 00:00:00 2001 From: sevenzing Date: Tue, 21 Apr 2026 21:46:41 +0400 Subject: [PATCH 21/21] oops forgot --- docs/ensnode.io/ensapi-openapi.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/ensnode.io/ensapi-openapi.json b/docs/ensnode.io/ensapi-openapi.json index 7e1761c49b..1e73071d5f 100644 --- a/docs/ensnode.io/ensapi-openapi.json +++ b/docs/ensnode.io/ensapi-openapi.json @@ -1095,11 +1095,11 @@ { "schema": { "type": "boolean", - "description": "Include detailed resolution trace information in the response.", + "description": "Include detailed OpenTelemetry trace information about the resolution in the response.", "default": false }, "required": false, - "description": "Include detailed resolution trace information in the response.", + "description": "Include detailed OpenTelemetry trace information about the resolution in the response.", "name": "trace", "in": "query" }, @@ -1225,11 +1225,11 @@ { "schema": { "type": "boolean", - "description": "Include detailed resolution trace information in the response.", + "description": "Include detailed OpenTelemetry trace information about the resolution in the response.", "default": false }, "required": false, - "description": "Include detailed resolution trace information in the response.", + "description": "Include detailed OpenTelemetry trace information about the resolution in the response.", "name": "trace", "in": "query" }, @@ -1335,11 +1335,11 @@ { "schema": { "type": "boolean", - "description": "Include detailed resolution trace information in the response.", + "description": "Include detailed OpenTelemetry trace information about the resolution in the response.", "default": false }, "required": false, - "description": "Include detailed resolution trace information in the response.", + "description": "Include detailed OpenTelemetry trace information about the resolution in the response.", "name": "trace", "in": "query" },