diff --git a/docs/ensnode.io/config/integrations/starlight/sidebar-topics/integrate.ts b/docs/ensnode.io/config/integrations/starlight/sidebar-topics/integrate.ts index 18b7500aa..bcfd7d91a 100644 --- a/docs/ensnode.io/config/integrations/starlight/sidebar-topics/integrate.ts +++ b/docs/ensnode.io/config/integrations/starlight/sidebar-topics/integrate.ts @@ -1,3 +1,5 @@ +import { OMNIGRAPH_EXAMPLES_SIDEBAR_ITEMS } from "../../../../src/data/omnigraph-examples/config.ts"; + export const integrateSidebarTopic = { id: "integrate-with-ensv2", label: "Integrate with ENSv2", @@ -97,60 +99,7 @@ export const integrateSidebarTopic = { { label: "Examples", collapsed: true, - items: [ - { - label: "Overview", - link: "/docs/integrate/omnigraph/examples", - }, - { - label: "Domain By Name", - link: "/docs/integrate/omnigraph/examples/domain-by-name", - }, - { - label: "Find Domains", - link: "/docs/integrate/omnigraph/examples/find-domains", - }, - { - label: "Domain Subdomains", - link: "/docs/integrate/omnigraph/examples/domain-subdomains", - }, - { - label: "Domain Events", - link: "/docs/integrate/omnigraph/examples/domain-events", - }, - { - label: "Account Domains", - link: "/docs/integrate/omnigraph/examples/domains-by-address", - }, - { - label: "Account Events", - link: "/docs/integrate/omnigraph/examples/account-events", - }, - { - label: "Registry Domains", - link: "/docs/integrate/omnigraph/examples/registry-domains", - }, - { - label: "Permissions By Contract", - link: "/docs/integrate/omnigraph/examples/permissions-by-contract", - }, - { - label: "Permissions By User", - link: "/docs/integrate/omnigraph/examples/permissions-by-user", - }, - { - label: "Account Resolver Permissions", - link: "/docs/integrate/omnigraph/examples/account-resolver-permissions", - }, - { - label: "Domain Resolver", - link: "/docs/integrate/omnigraph/examples/domain-resolver", - }, - { - label: "Namegraph", - link: "/docs/integrate/omnigraph/examples/namegraph", - }, - ], + items: OMNIGRAPH_EXAMPLES_SIDEBAR_ITEMS, }, { label: "Schema Reference", diff --git a/docs/ensnode.io/scripts/fetch-omnigraph-example-responses.mts b/docs/ensnode.io/scripts/fetch-omnigraph-example-responses.mts index 5cb4de449..5f7502f33 100644 --- a/docs/ensnode.io/scripts/fetch-omnigraph-example-responses.mts +++ b/docs/ensnode.io/scripts/fetch-omnigraph-example-responses.mts @@ -2,18 +2,28 @@ import { existsSync, readFileSync, writeFileSync } from "node:fs"; import { dirname, join } from "node:path"; import { fileURLToPath } from "node:url"; -import { OMNIGRAPH_EXAMPLES_META } from "../src/data/omnigraph-examples/meta.ts"; +import { + getOmnigraphExampleConfigById, + OMNIGRAPH_EXAMPLES_CONFIG, +} from "../src/data/omnigraph-examples/config.ts"; import type { SnapshotExample } from "../src/data/omnigraph-examples/types.ts"; -import { ENSNODE_URL } from "../src/lib/examples/omnigraph/constants.ts"; +import { getDocsOmnigraphNamespaceConfig } from "../src/lib/examples/omnigraph/constants.ts"; function logStep(message: string, id?: string) { console.log(`[omnigraph-examples] ${message} ${id ? `for '${id}'` : ""}`); } +function logWarn(message: string, id?: string) { + console.warn(`[omnigraph-examples] WARN: ${message} ${id ? `for example '${id}'` : ""}`); +} + function logError(message: string, id?: string) { console.error(`[omnigraph-examples] ERROR: ${message} ${id ? `for example '${id}'` : ""}`); } +const TIMEOUT_MS = 120_000; +const WARN_THRESHOLD_MS = 5_000; + const dataDir = join(dirname(fileURLToPath(import.meta.url)), "../src/data/omnigraph-examples"); const examplesPath = join(dataDir, "examples.json"); const outputPath = join(dataDir, "responses.json"); @@ -27,8 +37,8 @@ const snapshotById = new Map( (JSON.parse(readFileSync(examplesPath, "utf8")) as SnapshotExample[]).map((e) => [e.id, e]), ); -// Only fetch responses for the rendered set: meta entries supported by the vendored snapshot. -const allExampleIds = (Object.keys(OMNIGRAPH_EXAMPLES_META) as string[]) +// Only fetch responses for the rendered set: config entries supported by the vendored snapshot. +const allExampleIds = OMNIGRAPH_EXAMPLES_CONFIG.map((config) => config.id) .filter((id) => snapshotById.has(id)) .sort(); @@ -49,14 +59,10 @@ if (argIds.length > 0) { const exampleIds = argIds.length > 0 ? argIds : allExampleIds; -// Endpoint defaults to the production v2 Sepolia URL; override to fill responses from a -// staged deployment (e.g. blue/green) before that version is promoted to the prod URL. -const url = new URL("/api/omnigraph", process.env.OMNIGRAPH_ENDPOINT ?? ENSNODE_URL).toString(); - logStep( argIds.length > 0 - ? `Refreshing ${exampleIds.length} of ${allExampleIds.length} examples from ${url}: ${exampleIds.join(", ")}` - : `Fetching all ${exampleIds.length} Omnigraph examples from ${url}`, + ? `Refreshing ${exampleIds.length} of ${allExampleIds.length} examples: ${exampleIds.join(", ")}` + : `Fetching all ${exampleIds.length} Omnigraph examples (per-example namespace endpoints)`, ); // When refreshing a subset, load the existing responses so unaffected entries are preserved. @@ -69,14 +75,27 @@ for (const id of exampleIds) { logStep("Getting example query", id); const example = snapshotById.get(id)!; + const config = getOmnigraphExampleConfigById(id); + if (!config) { + logError(`No OMNIGRAPH_EXAMPLES_CONFIG entry for id`, id); + process.exit(1); + } + + const endpointOverride = process.env.OMNIGRAPH_ENDPOINT; + const baseUrl = endpointOverride ?? getDocsOmnigraphNamespaceConfig(config.namespace).ensnodeUrl; + const url = new URL("/api/omnigraph", baseUrl).toString(); + const query = example.query.trim(); const variables = example.variables; + logStep(`POST ${url}`, id); + + const started = performance.now(); const response = await fetch(url, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ query, variables }), - signal: AbortSignal.timeout(120_000), + signal: AbortSignal.timeout(TIMEOUT_MS), }); if (!response.ok) { @@ -85,21 +104,23 @@ for (const id of exampleIds) { process.exit(1); } - const body = await response.json(); + const body = (await response.json()) as Record; + const durationMs = Math.round(performance.now() - started); - if ( - typeof body === "object" && - body !== null && - "errors" in body && - Array.isArray((body as { errors: unknown }).errors) && - (body as { errors: unknown[] }).errors.length > 0 - ) { + if ("errors" in body && Array.isArray(body.errors) && body.errors.length > 0) { logError(`GraphQL errors: ${JSON.stringify(body, null, 2)}`, id); process.exit(1); } + if (durationMs > WARN_THRESHOLD_MS) { + logWarn( + `Took ${durationMs}ms (threshold ${WARN_THRESHOLD_MS}ms). Omnigraph examples should stay fast. Consider changing input variables or simplifying the example query.`, + id, + ); + } + out[id] = body; - logStep("Success", id); + logStep(`Success in ${durationMs}ms`, id); } logStep(`Writing responses to ${outputPath}`); diff --git a/docs/ensnode.io/scripts/snapshot-omnigraph-version.mts b/docs/ensnode.io/scripts/snapshot-omnigraph-version.mts index 40ee6e2c5..e385d03ff 100644 --- a/docs/ensnode.io/scripts/snapshot-omnigraph-version.mts +++ b/docs/ensnode.io/scripts/snapshot-omnigraph-version.mts @@ -6,10 +6,10 @@ import { fileURLToPath } from "node:url"; import { buildSchema, parse, validate } from "graphql"; import { getNamespaceSpecificValue } from "@ensnode/ensnode-sdk"; -import { GRAPHQL_API_EXAMPLE_QUERIES } from "@ensnode/ensnode-sdk/internal"; +import { GRAPHQL_API_EXAMPLE_QUERIES as EXAMPLE_QUERIES_FROM_SDK } from "@ensnode/ensnode-sdk/internal"; +import { OMNIGRAPH_EXAMPLES_CONFIG } from "../src/data/omnigraph-examples/config.ts"; import type { SnapshotExample } from "../src/data/omnigraph-examples/types.ts"; -import { DOCS_OMNIGRAPH_NAMESPACE, ENSNODE_URL } from "../src/lib/examples/omnigraph/constants.ts"; // Freeze the CURRENT workspace SDK omnigraph bundle (examples + schema) into the single // vendored snapshot the docs render. Run this on the release commit of , where the @@ -44,11 +44,24 @@ const sdkVersion = ( } ).version; -const examples: SnapshotExample[] = GRAPHQL_API_EXAMPLE_QUERIES.map((ex) => ({ - id: ex.id, - query: ex.query.trim(), - variables: getNamespaceSpecificValue(DOCS_OMNIGRAPH_NAMESPACE, ex.variables), -})); +const sdkExampleById = new Map(EXAMPLE_QUERIES_FROM_SDK.map((ex) => [ex.id, ex])); + +const examples: SnapshotExample[] = OMNIGRAPH_EXAMPLES_CONFIG.flatMap((config) => { + const ex = sdkExampleById.get(config.id); + if (!ex) { + console.error( + `[omnigraph:snapshot] Missing SDK example query ID for config entry: ${config.id}`, + ); + process.exit(1); + } + return [ + { + id: config.id, + query: ex.query.trim(), + variables: getNamespaceSpecificValue(config.namespace, ex.variables), + }, + ]; +}); // Fail fast if any example is invalid against this version's schema. const invalid = examples.flatMap((ex) => { @@ -71,7 +84,7 @@ writeFileSync(join(dataDir, "schema.graphql"), sdl, "utf8"); writeFileSync(join(dataDir, "examples.json"), `${JSON.stringify(examples, null, 2)}\n`, "utf8"); writeFileSync( join(dataDir, "snapshot.json"), - `${JSON.stringify({ version, commit, sdkVersion, schemaTag: version, endpoint: ENSNODE_URL, snapshottedAt: new Date().toISOString().slice(0, 10) }, null, 2)}\n`, + `${JSON.stringify({ version, commit, sdkVersion, schemaTag: version, snapshottedAt: new Date().toISOString().slice(0, 10) }, null, 2)}\n`, "utf8", ); diff --git a/docs/ensnode.io/src/components/molecules/omnigraph-static-example/CurlStaticCodeExample.astro b/docs/ensnode.io/src/components/molecules/omnigraph-static-example/CurlStaticCodeExample.astro index 8796338e5..cd068ef15 100644 --- a/docs/ensnode.io/src/components/molecules/omnigraph-static-example/CurlStaticCodeExample.astro +++ b/docs/ensnode.io/src/components/molecules/omnigraph-static-example/CurlStaticCodeExample.astro @@ -6,9 +6,11 @@ interface Props { uid: string; curlExample: string; responseJson: string | null; + responseSourceLabel: string; + responseSourceDocUrl: string; } -const { uid, curlExample, responseJson } = Astro.props; +const { uid, curlExample, responseJson, responseSourceLabel, responseSourceDocUrl } = Astro.props; --- ) : null diff --git a/docs/ensnode.io/src/components/molecules/omnigraph-static-example/GraphqlStaticQueryExample.astro b/docs/ensnode.io/src/components/molecules/omnigraph-static-example/GraphqlStaticQueryExample.astro index eb2dc2ab2..240c9d941 100644 --- a/docs/ensnode.io/src/components/molecules/omnigraph-static-example/GraphqlStaticQueryExample.astro +++ b/docs/ensnode.io/src/components/molecules/omnigraph-static-example/GraphqlStaticQueryExample.astro @@ -2,8 +2,6 @@ /** * Omnigraph panel: GraphQL request is query + variables; result is a separate output section. */ -import type { ENSNamespaceId } from "@ensnode/ensnode-sdk"; - import StaticExampleActionBar from "./StaticExampleActionBar.astro"; import StaticExampleCodeSection from "./StaticExampleCodeSection.astro"; import StaticExampleOutputSection from "./StaticExampleOutputSection.astro"; @@ -15,7 +13,8 @@ interface Props { responseJson: string | null; adminUrl: string; hostedInstanceDocUrl: string; - hostedInstanceNamespace: ENSNamespaceId; + hostedInstanceLabel: string; + responseSourceLabel: string; } const { @@ -25,7 +24,8 @@ const { responseJson, adminUrl, hostedInstanceDocUrl, - hostedInstanceNamespace, + hostedInstanceLabel, + responseSourceLabel, } = Astro.props; --- @@ -33,7 +33,7 @@ const { variant="ensadmin" adminUrl={adminUrl} hostedInstanceDocUrl={hostedInstanceDocUrl} - hostedInstanceNamespace={hostedInstanceNamespace} + hostedInstanceLabel={hostedInstanceLabel} /> + ) : null } diff --git a/docs/ensnode.io/src/components/molecules/omnigraph-static-example/SdkStaticCodeExample.astro b/docs/ensnode.io/src/components/molecules/omnigraph-static-example/SdkStaticCodeExample.astro index 19a5ed884..defdbabae 100644 --- a/docs/ensnode.io/src/components/molecules/omnigraph-static-example/SdkStaticCodeExample.astro +++ b/docs/ensnode.io/src/components/molecules/omnigraph-static-example/SdkStaticCodeExample.astro @@ -1,5 +1,4 @@ --- -import type { ENSNamespaceId } from "@ensnode/ensnode-sdk"; import type { SetupPackageManager } from "@lib/examples/omnigraph/build-integration-snippets"; import StaticExampleActionBar from "./StaticExampleActionBar.astro"; @@ -46,7 +45,8 @@ interface Props { setupSnippets: Record; responseJson: string | null; hostedInstanceDocUrl: string; - hostedInstanceNamespace: ENSNamespaceId; + hostedInstanceLabel: string; + responseSourceLabel: string; } const { @@ -57,7 +57,8 @@ const { setupSnippets, responseJson, hostedInstanceDocUrl, - hostedInstanceNamespace, + hostedInstanceLabel, + responseSourceLabel, } = Astro.props; const config = SDK_PANEL_CONFIG[integration]; @@ -74,7 +75,7 @@ const docsLinkClass = "underline underline-offset-2 hover:text-[var(--sl-color-t @@ -85,6 +86,8 @@ const docsLinkClass = "underline underline-offset-2 hover:text-[var(--sl-color-t ) : null diff --git a/docs/ensnode.io/src/components/molecules/omnigraph-static-example/StaticExampleActionBar.astro b/docs/ensnode.io/src/components/molecules/omnigraph-static-example/StaticExampleActionBar.astro index b074cc0b1..4c9c27205 100644 --- a/docs/ensnode.io/src/components/molecules/omnigraph-static-example/StaticExampleActionBar.astro +++ b/docs/ensnode.io/src/components/molecules/omnigraph-static-example/StaticExampleActionBar.astro @@ -1,6 +1,4 @@ --- -import type { ENSNamespaceId } from "@ensnode/ensnode-sdk"; - import ENSAdminCTAButton from "@components/atoms/ENSAdminCTAButton.astro"; import StackBlitzLogo from "@components/atoms/logos/astro/StackBlitzLogo.astro"; @@ -10,11 +8,11 @@ interface Props { variant: "ensadmin" | "stackblitz"; adminUrl?: string; hostedInstanceDocUrl: string; - hostedInstanceNamespace: ENSNamespaceId; + hostedInstanceLabel: string; staticExampleId?: string; } -const { variant, adminUrl, hostedInstanceDocUrl, hostedInstanceNamespace, staticExampleId } = +const { variant, adminUrl, hostedInstanceDocUrl, hostedInstanceLabel, staticExampleId } = Astro.props; const actionBarClass = @@ -37,6 +35,6 @@ const actionButtonClass = diff --git a/docs/ensnode.io/src/components/molecules/omnigraph-static-example/StaticExampleOutputSection.astro b/docs/ensnode.io/src/components/molecules/omnigraph-static-example/StaticExampleOutputSection.astro index f1a9c1efd..2d198a707 100644 --- a/docs/ensnode.io/src/components/molecules/omnigraph-static-example/StaticExampleOutputSection.astro +++ b/docs/ensnode.io/src/components/molecules/omnigraph-static-example/StaticExampleOutputSection.astro @@ -3,19 +3,27 @@ import { Code } from "@astrojs/starlight/components"; import StaticExampleNote from "./StaticExampleNote.astro"; import { + staticExampleHostedInstanceLinkClass, staticExampleOutputCodeWrapClass, staticExampleOutputMaxHeight, - staticExampleOutputSnapshotNote, staticExampleSectionXClass, } from "./constants"; interface Props { uid: string; responseJson: string; + responseSourceLabel: string; + responseSourceDocUrl: string; heading?: string; } -const { uid, responseJson, heading = "Output" } = Astro.props; +const { + uid, + responseJson, + responseSourceLabel, + responseSourceDocUrl, + heading = "Output", +} = Astro.props; const badgeClass = "inline-flex items-center rounded-full bg-[var(--sl-color-gray-5)] px-2.5 py-0.5 text-[0.7rem] font-medium tracking-wide text-[var(--sl-color-gray-2)] dark:bg-[var(--sl-color-gray-6)]"; @@ -35,5 +43,10 @@ const headingId = `${uid}-output-heading`; style={`max-height: ${staticExampleOutputMaxHeight}`}> - {staticExampleOutputSnapshotNote} + + Output matches a point in time snapshot GraphQL response from our{" "} + {responseSourceLabel} ENSNode instance. Live output depends on the configuration of your ENSNode instance and ENS state updates. + diff --git a/docs/ensnode.io/src/components/molecules/omnigraph-static-example/StaticExamplePlaygroundHint.astro b/docs/ensnode.io/src/components/molecules/omnigraph-static-example/StaticExamplePlaygroundHint.astro index 17d8908e7..d608478bf 100644 --- a/docs/ensnode.io/src/components/molecules/omnigraph-static-example/StaticExamplePlaygroundHint.astro +++ b/docs/ensnode.io/src/components/molecules/omnigraph-static-example/StaticExamplePlaygroundHint.astro @@ -1,16 +1,13 @@ --- -import type { ENSNamespaceId } from "@ensnode/ensnode-sdk"; +import { staticExampleHostedInstanceLinkClass } from "./constants"; interface Props { variant: "ensadmin" | "stackblitz"; hostedInstanceDocUrl: string; - hostedInstanceNamespace: ENSNamespaceId; + hostedInstanceLabel: string; } -const { variant, hostedInstanceDocUrl, hostedInstanceNamespace } = Astro.props; - -const hostedInstanceLinkClass = - "text-sm font-semibold text-[var(--sl-color-accent)] underline underline-offset-4 hover:underline-offset-2 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[var(--sl-color-text-accent)] transition-[text-underline-offset] duration-200"; +const { variant, hostedInstanceDocUrl, hostedInstanceLabel } = Astro.props; ---