77 */
88
99import type { Metadata , QuickstartsFrontmatter } from "~/content.config.ts"
10- import { productChainLinks , chainNames , type ProductData } from "~/components/QuickLinks/data/productChainLinks.ts"
10+
11+ /**
12+ * Base URLs - Environment-aware constants
13+ * These should match the production URLs for structured data consistency
14+ */
15+ export const DOCS_BASE_URL = "https://docs.chain.link"
16+ export const CHAINLINK_BASE_URL = "https://chain.link"
1117
1218// Schema.org compliant types
1319export type SchemaType = "TechArticle" | "HowTo" | "APIReference" | "BreadcrumbList" | "Organization" | "WebSite"
@@ -38,10 +44,10 @@ export interface VersionInfo {
3844export const CHAINLINK_ORGANIZATION = {
3945 "@type" : "Organization" ,
4046 name : "Chainlink Labs" ,
41- url : "https://chain.link" ,
47+ url : CHAINLINK_BASE_URL ,
4248 logo : {
4349 "@type" : "ImageObject" ,
44- url : "https://docs.chain.link/ images/logo.png" ,
50+ url : ` ${ DOCS_BASE_URL } / images/logo.png` ,
4551 width : 200 ,
4652 height : 200 ,
4753 } ,
@@ -58,7 +64,7 @@ export const CHAINLINK_ORGANIZATION = {
5864 {
5965 "@type" : "ContactPoint" ,
6066 contactType : "customer support" ,
61- url : "https://chain.link/ support" ,
67+ url : ` ${ CHAINLINK_BASE_URL } / support` ,
6268 } ,
6369 {
6470 "@type" : "ContactPoint" ,
@@ -82,10 +88,10 @@ export const CHAINLINK_ORGANIZATION = {
8288export const CHAINLINK_PUBLISHER = {
8389 "@type" : "Organization" ,
8490 name : "Chainlink Documentation" ,
85- url : "https://docs.chain.link" ,
91+ url : DOCS_BASE_URL ,
8692 logo : {
8793 "@type" : "ImageObject" ,
88- url : "https://docs.chain.link/ images/logo.png" ,
94+ url : ` ${ DOCS_BASE_URL } / images/logo.png` ,
8995 width : 200 ,
9096 height : 200 ,
9197 } ,
@@ -281,75 +287,6 @@ export function detectQuickstartProducts(products?: string[]): string[] {
281287 return products . map ( ( product ) => productMap [ product . toLowerCase ( ) ] ) . filter ( Boolean )
282288}
283289
284- /**
285- * Get supported networks for a single Chainlink product
286- * Returns comprehensive list of all networks the product supports
287- */
288- export function getSupportedNetworks ( pathname ?: string ) : string [ ] {
289- const product = detectChainlinkProduct ( pathname )
290- if ( ! product || ! productChainLinks [ product ] ) return [ ]
291-
292- const productData = productChainLinks [ product ] as ProductData
293- if ( ! productData . chains ) return [ ]
294-
295- // Get all supported chain keys and map to display names
296- const supportedChains = Object . keys ( productData . chains )
297- return supportedChains
298- . map ( ( chainKey ) => chainNames [ chainKey ] || chainKey )
299- . filter ( Boolean )
300- . sort ( )
301- }
302-
303- /**
304- * Get intersection of supported networks for multiple products
305- * Returns networks that ALL specified products support
306- */
307- export function getIntersectionOfSupportedNetworks ( products : string [ ] ) : string [ ] {
308- if ( products . length === 0 ) return [ ]
309- if ( products . length === 1 ) {
310- // Single product - get all its networks
311- const product = products [ 0 ]
312- if ( ! productChainLinks [ product ] ) return [ ]
313-
314- const productData = productChainLinks [ product ] as ProductData
315- if ( ! productData . chains ) return [ ]
316-
317- const supportedChains = Object . keys ( productData . chains )
318- return supportedChains
319- . map ( ( chainKey ) => chainNames [ chainKey ] || chainKey )
320- . filter ( Boolean )
321- . sort ( )
322- }
323-
324- // Multiple products - find intersection
325- const networkSets = products . map ( ( product ) => {
326- if ( ! productChainLinks [ product ] ) return new Set < string > ( )
327-
328- const productData = productChainLinks [ product ] as ProductData
329- if ( ! productData . chains ) return new Set < string > ( )
330-
331- const chains = Object . keys ( productData . chains )
332- . map ( ( chainKey ) => chainNames [ chainKey ] || chainKey )
333- . filter ( Boolean )
334-
335- return new Set ( chains )
336- } )
337-
338- // Start with first product's networks
339- const intersection = networkSets [ 0 ]
340-
341- // Keep only networks that exist in ALL products
342- for ( let i = 1 ; i < networkSets . length ; i ++ ) {
343- for ( const network of intersection ) {
344- if ( ! networkSets [ i ] . has ( network ) ) {
345- intersection . delete ( network )
346- }
347- }
348- }
349-
350- return Array . from ( intersection ) . sort ( )
351- }
352-
353290/**
354291 * Extract target platform from metadata content
355292 * Returns Schema.org compliant target platform
@@ -431,8 +368,6 @@ export function extractToolsAndPrerequisites(
431368 remix : "Remix IDE" ,
432369 metamask : "MetaMask" ,
433370 foundry : "Foundry" ,
434- truffle : "Truffle" ,
435- ganache : "Ganache" ,
436371 web3 : "Web3.js" ,
437372 ethers : "Ethers.js" ,
438373 viem : "Viem" ,
@@ -520,7 +455,6 @@ export function generateTechArticle(
520455 const { isLearningResource, category } = detectContentType ( pathname )
521456 const difficulty = extractDifficulty ( metadata ?. excerpt , pathname )
522457 const product = detectChainlinkProduct ( pathname )
523- const supportedNetworks = getSupportedNetworks ( pathname )
524458
525459 const baseArticle = {
526460 "@context" : "https://schema.org" ,
@@ -540,8 +474,8 @@ export function generateTechArticle(
540474 url : metadata ?. image
541475 ? metadata . image . startsWith ( "http" )
542476 ? metadata . image
543- : `https://docs.chain.link ${ metadata . image } `
544- : "https://docs.chain.link/ images/logo.png" ,
477+ : `${ DOCS_BASE_URL } ${ metadata . image } `
478+ : ` ${ DOCS_BASE_URL } / images/logo.png` ,
545479 width : 1200 ,
546480 height : 630 ,
547481 } ,
@@ -556,13 +490,15 @@ export function generateTechArticle(
556490 ? `Smart contract and blockchain development using Chainlink ${ product } `
557491 : "Smart contract and blockchain development using Chainlink" ,
558492 } ,
559- // Add supported networks as mentions if available
560- ...( supportedNetworks . length > 0 && {
561- mentions : supportedNetworks . map ( ( network ) => ( {
562- "@type" : "Thing" ,
563- name : network ,
564- description : `${ network } blockchain network` ,
565- } ) ) ,
493+ // Add Chainlink product mention for better topical SEO
494+ ...( product && {
495+ mentions : [
496+ {
497+ "@type" : "Thing" ,
498+ name : product ,
499+ description : `Chainlink ${ product } ` ,
500+ } ,
501+ ] ,
566502 } ) ,
567503 }
568504
@@ -600,7 +536,6 @@ export function generateHowTo(
600536 const { tools, prerequisites } = extractToolsAndPrerequisites ( metadata ?. excerpt , pathname )
601537 const duration = parseTimeToISO8601 ( estimatedTime )
602538 const product = detectChainlinkProduct ( pathname )
603- const supportedNetworks = getSupportedNetworks ( pathname )
604539
605540 return {
606541 "@context" : "https://schema.org" ,
@@ -621,8 +556,8 @@ export function generateHowTo(
621556 url : metadata ?. image
622557 ? metadata . image . startsWith ( "http" )
623558 ? metadata . image
624- : `https://docs.chain.link ${ metadata . image } `
625- : "https://docs.chain.link/ images/logo.png" ,
559+ : `${ DOCS_BASE_URL } ${ metadata . image } `
560+ : ` ${ DOCS_BASE_URL } / images/logo.png` ,
626561 width : 1200 ,
627562 height : 630 ,
628563 } ,
@@ -657,13 +592,15 @@ export function generateHowTo(
657592 ? `Building decentralized applications with Chainlink ${ product } `
658593 : "Building decentralized applications with Chainlink" ,
659594 } ,
660- // Add supported networks as mentions if available
661- ...( supportedNetworks . length > 0 && {
662- mentions : supportedNetworks . map ( ( network ) => ( {
663- "@type" : "Thing" ,
664- name : network ,
665- description : `${ network } blockchain network` ,
666- } ) ) ,
595+ // Add Chainlink product mention for better topical SEO
596+ ...( product && {
597+ mentions : [
598+ {
599+ "@type" : "Thing" ,
600+ name : product ,
601+ description : `Chainlink ${ product } ` ,
602+ } ,
603+ ] ,
667604 } ) ,
668605 }
669606}
@@ -682,7 +619,6 @@ export function generateAPIReference(
682619 const programmingModel = extractProgrammingModel ( metadata ?. excerpt , pathname )
683620 const targetPlatform = extractTargetPlatform ( metadata ?. excerpt , pathname )
684621 const product = detectChainlinkProduct ( pathname )
685- const supportedNetworks = getSupportedNetworks ( pathname )
686622
687623 // Use provided version info or extract from metadata/pathname
688624 const versionMatch = pathname . match ( / v ( \d + \. \d + \. \d + ) / )
@@ -712,8 +648,8 @@ export function generateAPIReference(
712648 url : metadata ?. image
713649 ? metadata . image . startsWith ( "http" )
714650 ? metadata . image
715- : `https://docs.chain.link ${ metadata . image } `
716- : "https://docs.chain.link/ images/logo.png" ,
651+ : `${ DOCS_BASE_URL } ${ metadata . image } `
652+ : ` ${ DOCS_BASE_URL } / images/logo.png` ,
717653 width : 1200 ,
718654 height : 630 ,
719655 } ,
@@ -729,34 +665,49 @@ export function generateAPIReference(
729665 ? `${ product } - Decentralized oracle network for smart contracts`
730666 : "Decentralized oracle network for smart contracts" ,
731667 } ,
732- // Add supported networks as mentions if available
733- ...( supportedNetworks . length > 0 && {
734- mentions : supportedNetworks . map ( ( network ) => ( {
735- "@type" : "Thing" ,
736- name : network ,
737- description : `${ network } blockchain network` ,
738- } ) ) ,
668+ // Add Chainlink product mention for better topical SEO
669+ ...( product && {
670+ mentions : [
671+ {
672+ "@type" : "Thing" ,
673+ name : product ,
674+ description : `Chainlink ${ product } ` ,
675+ } ,
676+ ] ,
739677 } ) ,
678+
740679 // Add version-specific structured data if available
741680 ...( versionInfo && {
742681 version,
743- ...( versionInfo . availableVersions && {
744- isPartOf : {
745- "@type" : "SoftwareApplication" ,
746- name : product ? `Chainlink ${ product } ` : "Chainlink Protocol" ,
747- versionList : versionInfo . availableVersions . map ( ( v ) => ( {
748- "@type" : "SoftwareVersion" ,
749- version : v ,
750- releaseDate : v === versionInfo . version ? releaseDate : undefined ,
751- status :
752- v === versionInfo . version && versionInfo . isLatest
753- ? "Latest"
754- : versionInfo . isDeprecated
755- ? "Deprecated"
756- : "Stable" ,
757- } ) ) ,
758- } ,
759- } ) ,
682+ isPartOf : {
683+ "@type" : "SoftwareApplication" ,
684+ name : product ? `Chainlink ${ product } ` : "Chainlink Protocol" ,
685+ description : product
686+ ? `${ product } - Decentralized oracle network for smart contracts`
687+ : "Decentralized oracle network for smart contracts" ,
688+ operatingSystem : "Blockchain" ,
689+ applicationCategory : "DeveloperApplication" ,
690+ url : `${ DOCS_BASE_URL } /${ product ?. toLowerCase ( ) } /api-reference/` ,
691+ ...( releaseDate && {
692+ datePublished : releaseDate ,
693+ dateModified : releaseDate ,
694+ } ) ,
695+ // Valid Schema.org properties only
696+ ...( versionInfo . availableVersions &&
697+ versionInfo . availableVersions . length > 1 && {
698+ // Use 'version' property (valid for CreativeWork parent)
699+ version,
700+ // Use 'isRelatedTo' for version relationships (valid for SoftwareApplication via Service inheritance)
701+ isRelatedTo : versionInfo . availableVersions
702+ . filter ( ( v ) => v !== version )
703+ . slice ( 0 , 3 ) // Limit for performance
704+ . map ( ( v ) => ( {
705+ "@type" : "SoftwareApplication" ,
706+ name : `${ product ? `Chainlink ${ product } ` : "Chainlink Protocol" } ${ v } ` ,
707+ url : version && version !== v ? resolvedCanonicalUrl . replace ( version , v ) : resolvedCanonicalUrl ,
708+ } ) ) ,
709+ } ) ,
710+ } ,
760711 mainEntityOfPage : {
761712 "@type" : "WebPage" ,
762713 "@id" : resolvedCanonicalUrl ,
@@ -845,7 +796,6 @@ export function generateQuickstartHowTo(
845796
846797 // Detect products from curated quickstart metadata
847798 const detectedProducts = detectQuickstartProducts ( frontmatter . products )
848- const supportedNetworks = getIntersectionOfSupportedNetworks ( detectedProducts )
849799 const primaryProduct = detectedProducts . length > 0 ? detectedProducts [ 0 ] : null
850800
851801 // Add requires to prerequisites if not already detected
@@ -872,7 +822,7 @@ export function generateQuickstartHowTo(
872822 ...( duration && { totalTime : duration } ) ,
873823 image : {
874824 "@type" : "ImageObject" ,
875- url : `https://docs.chain.link /images/quickstarts/feature/${ frontmatter . image } ` ,
825+ url : `${ DOCS_BASE_URL } /images/quickstarts/feature/${ frontmatter . image } ` ,
876826 width : 1200 ,
877827 height : 630 ,
878828 } ,
@@ -910,23 +860,21 @@ export function generateQuickstartHowTo(
910860 // Quickstart-specific properties
911861 genre : "Quickstart Guide" ,
912862 ...( frontmatter . githubSourceCodeUrl && {
913- codeRepository : frontmatter . githubSourceCodeUrl ,
863+ workExample : {
864+ "@type" : "SoftwareSourceCode" ,
865+ name : `${ frontmatter . title } - Source Code` ,
866+ description : "Complete source code and examples for this tutorial" ,
867+ codeRepository : frontmatter . githubSourceCodeUrl ,
868+ url : frontmatter . githubSourceCodeUrl ,
869+ } ,
870+ } ) ,
871+ // Add Chainlink product mentions for better topical SEO
872+ ...( frontmatter . products && {
873+ mentions : frontmatter . products . map ( ( product ) => ( {
874+ "@type" : "Thing" ,
875+ name : product === "general" ? "Chainlink" : product . toUpperCase ( ) ,
876+ } ) ) ,
914877 } ) ,
915- // Add supported networks as mentions if available, otherwise use frontmatter products
916- ...( supportedNetworks . length > 0
917- ? {
918- mentions : supportedNetworks . map ( ( network ) => ( {
919- "@type" : "Thing" ,
920- name : network ,
921- description : `${ network } blockchain network` ,
922- } ) ) ,
923- }
924- : frontmatter . products && {
925- mentions : frontmatter . products . map ( ( product ) => ( {
926- "@type" : "Thing" ,
927- name : product === "general" ? "Chainlink" : product . toUpperCase ( ) ,
928- } ) ) ,
929- } ) ,
930878 }
931879}
932880
0 commit comments