chore: import upstream snapshot with attribution
CI / Migrate Dev DB (push) Has been skipped
CI / Detect Version (push) Has been cancelled
CI / Migrate DB (push) Has been cancelled
CI / Build Dev ECR (./docker/app.Dockerfile, ECR_APP) (push) Has been cancelled
CI / Build Dev ECR (./docker/db.Dockerfile, ECR_MIGRATIONS) (push) Has been cancelled
CI / Build Dev ECR (./docker/pii.Dockerfile, ECR_PII) (push) Has been cancelled
CI / Build Dev ECR (./docker/realtime.Dockerfile, ECR_REALTIME) (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
CI / Build AMD64 (./docker/app.Dockerfile, ECR_APP, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build AMD64 (./docker/db.Dockerfile, ECR_MIGRATIONS, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (./docker/pii.Dockerfile, ECR_PII, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (./docker/realtime.Dockerfile, ECR_REALTIME, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/app.Dockerfile, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/db.Dockerfile, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/pii.Dockerfile, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/realtime.Dockerfile, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Test and Build (push) Has been cancelled
Publish CLI Package / publish-npm (push) Has been cancelled
Publish Python SDK / publish-pypi (push) Has been cancelled
Publish TypeScript SDK / publish-npm (push) Has been cancelled
CI / Migrate Dev DB (push) Has been skipped
CI / Detect Version (push) Has been cancelled
CI / Migrate DB (push) Has been cancelled
CI / Build Dev ECR (./docker/app.Dockerfile, ECR_APP) (push) Has been cancelled
CI / Build Dev ECR (./docker/db.Dockerfile, ECR_MIGRATIONS) (push) Has been cancelled
CI / Build Dev ECR (./docker/pii.Dockerfile, ECR_PII) (push) Has been cancelled
CI / Build Dev ECR (./docker/realtime.Dockerfile, ECR_REALTIME) (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
CI / Build AMD64 (./docker/app.Dockerfile, ECR_APP, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build AMD64 (./docker/db.Dockerfile, ECR_MIGRATIONS, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (./docker/pii.Dockerfile, ECR_PII, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (./docker/realtime.Dockerfile, ECR_REALTIME, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/app.Dockerfile, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/db.Dockerfile, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/pii.Dockerfile, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/realtime.Dockerfile, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Test and Build (push) Has been cancelled
Publish CLI Package / publish-npm (push) Has been cancelled
Publish Python SDK / publish-pypi (push) Has been cancelled
Publish TypeScript SDK / publish-npm (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,193 @@
|
||||
import type { MetadataRoute } from 'next'
|
||||
import { getAllPostMeta as getAllBlogPostMeta } from '@/lib/blog/registry'
|
||||
import type { ContentMeta } from '@/lib/content/schema'
|
||||
import { latestModified } from '@/lib/content/utils'
|
||||
import { SITE_URL } from '@/lib/core/utils/urls'
|
||||
import { INTEGRATIONS, INTEGRATIONS_UPDATED_AT } from '@/lib/integrations'
|
||||
import { getAllPostMeta as getAllLibraryPostMeta } from '@/lib/library/registry'
|
||||
import {
|
||||
ALL_COMPETITORS,
|
||||
getLatestVerifiedDate,
|
||||
SIM_LATEST_VERIFIED,
|
||||
} from '@/app/(landing)/comparison/utils'
|
||||
import { ALL_CATALOG_MODELS, MODEL_PROVIDERS_WITH_CATALOGS } from '@/app/(landing)/models/utils'
|
||||
|
||||
/** One sitemap entry per author, timestamped by their most recently updated post. */
|
||||
function buildAuthorPages(posts: ContentMeta[], basePath: string): MetadataRoute.Sitemap {
|
||||
const authorsMap = new Map<string, Date>()
|
||||
for (const p of posts) {
|
||||
for (const author of p.authors ?? [p.author]) {
|
||||
const postDate = new Date(p.updated ?? p.date)
|
||||
const existing = authorsMap.get(author.id)
|
||||
if (!existing || postDate > existing) {
|
||||
authorsMap.set(author.id, postDate)
|
||||
}
|
||||
}
|
||||
}
|
||||
return [...authorsMap.entries()].map(([id, date]) => ({
|
||||
url: `${SITE_URL}${basePath}/authors/${id}`,
|
||||
lastModified: date,
|
||||
}))
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the public sitemap by composing static landing pages with the
|
||||
* dynamic catalogs (blog posts, library posts, authors, integrations, model
|
||||
* providers, and individual models). Per-integration entries are emitted
|
||||
* under `/integrations/{slug}` to match the landing route at
|
||||
* `app/(landing)/integrations/[slug]`; slugs are guaranteed unique
|
||||
* by the catalog generator in `scripts/generate-docs.ts`.
|
||||
*/
|
||||
export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
|
||||
const baseUrl = SITE_URL
|
||||
const [posts, libraryPosts] = await Promise.all([getAllBlogPostMeta(), getAllLibraryPostMeta()])
|
||||
|
||||
const latestPostDateValue = latestModified(posts)
|
||||
const latestLibraryPostDate = latestModified(libraryPosts)
|
||||
|
||||
const modelTimes = MODEL_PROVIDERS_WITH_CATALOGS.flatMap((provider) =>
|
||||
provider.models.map((model) => new Date(model.pricing.updatedAt).getTime())
|
||||
)
|
||||
const latestModelDate = modelTimes.length > 0 ? new Date(Math.max(...modelTimes)) : undefined
|
||||
|
||||
const integrationsUpdatedAt = new Date(`${INTEGRATIONS_UPDATED_AT}T00:00:00Z`)
|
||||
|
||||
const staticPages: MetadataRoute.Sitemap = [
|
||||
{
|
||||
url: baseUrl,
|
||||
},
|
||||
{
|
||||
url: `${baseUrl}/workflows`,
|
||||
},
|
||||
{
|
||||
url: `${baseUrl}/pricing`,
|
||||
},
|
||||
{
|
||||
url: `${baseUrl}/demo`,
|
||||
},
|
||||
{
|
||||
url: `${baseUrl}/contact`,
|
||||
},
|
||||
{
|
||||
url: `${baseUrl}/careers`,
|
||||
},
|
||||
{
|
||||
url: `${baseUrl}/enterprise`,
|
||||
},
|
||||
{
|
||||
url: `${baseUrl}/solutions/compliance`,
|
||||
},
|
||||
{
|
||||
url: `${baseUrl}/solutions/engineering`,
|
||||
},
|
||||
{
|
||||
url: `${baseUrl}/solutions/finance`,
|
||||
},
|
||||
{
|
||||
url: `${baseUrl}/solutions/hr`,
|
||||
},
|
||||
{
|
||||
url: `${baseUrl}/solutions/it`,
|
||||
},
|
||||
{
|
||||
url: `${baseUrl}/blog`,
|
||||
lastModified: latestPostDateValue,
|
||||
},
|
||||
{
|
||||
url: `${baseUrl}/blog/tags`,
|
||||
lastModified: latestPostDateValue,
|
||||
},
|
||||
{
|
||||
url: `${baseUrl}/library`,
|
||||
lastModified: latestLibraryPostDate,
|
||||
},
|
||||
{
|
||||
url: `${baseUrl}/library/tags`,
|
||||
lastModified: latestLibraryPostDate,
|
||||
},
|
||||
{
|
||||
url: `${baseUrl}/changelog`,
|
||||
lastModified: latestPostDateValue,
|
||||
},
|
||||
{
|
||||
url: `${baseUrl}/integrations`,
|
||||
lastModified: integrationsUpdatedAt,
|
||||
},
|
||||
{
|
||||
url: `${baseUrl}/models`,
|
||||
lastModified: latestModelDate,
|
||||
},
|
||||
{
|
||||
url: `${baseUrl}/terms`,
|
||||
lastModified: new Date('2024-10-14'),
|
||||
},
|
||||
{
|
||||
url: `${baseUrl}/privacy`,
|
||||
lastModified: new Date('2024-10-14'),
|
||||
},
|
||||
]
|
||||
|
||||
const blogPages: MetadataRoute.Sitemap = posts.map((p) => ({
|
||||
url: p.canonical,
|
||||
lastModified: new Date(p.updated ?? p.date),
|
||||
}))
|
||||
const authorPages = buildAuthorPages(posts, '/blog')
|
||||
|
||||
const libraryPages: MetadataRoute.Sitemap = libraryPosts.map((p) => ({
|
||||
url: p.canonical,
|
||||
lastModified: new Date(p.updated ?? p.date),
|
||||
}))
|
||||
const libraryAuthorPages = buildAuthorPages(libraryPosts, '/library')
|
||||
|
||||
const integrationPages: MetadataRoute.Sitemap = INTEGRATIONS.map((integration) => ({
|
||||
url: `${baseUrl}/integrations/${integration.slug}`,
|
||||
lastModified: integrationsUpdatedAt,
|
||||
}))
|
||||
|
||||
const providerPages: MetadataRoute.Sitemap = MODEL_PROVIDERS_WITH_CATALOGS.flatMap((provider) => {
|
||||
if (provider.models.length === 0) return []
|
||||
return [
|
||||
{
|
||||
url: `${baseUrl}${provider.href}`,
|
||||
lastModified: new Date(
|
||||
Math.max(...provider.models.map((model) => new Date(model.pricing.updatedAt).getTime()))
|
||||
),
|
||||
},
|
||||
]
|
||||
})
|
||||
|
||||
const modelEntries: MetadataRoute.Sitemap = ALL_CATALOG_MODELS.map((model) => ({
|
||||
url: `${baseUrl}${model.href}`,
|
||||
lastModified: new Date(model.pricing.updatedAt),
|
||||
}))
|
||||
|
||||
// Matches the max(Sim, competitor) verified-date logic each detail page's own
|
||||
// JSON-LD `dateModified` uses, so the sitemap timestamp never lags behind it.
|
||||
const competitorLastModified = (competitor: (typeof ALL_COMPETITORS)[number]) =>
|
||||
new Date(Math.max(SIM_LATEST_VERIFIED.getTime(), getLatestVerifiedDate(competitor).getTime()))
|
||||
|
||||
const comparisonLastModified =
|
||||
ALL_COMPETITORS.length > 0
|
||||
? new Date(Math.max(...ALL_COMPETITORS.map((c) => competitorLastModified(c).getTime())))
|
||||
: SIM_LATEST_VERIFIED
|
||||
|
||||
const comparisonPages: MetadataRoute.Sitemap = [
|
||||
{ url: `${baseUrl}/comparison`, lastModified: comparisonLastModified },
|
||||
...ALL_COMPETITORS.map((competitor) => ({
|
||||
url: `${baseUrl}/comparison/${competitor.id}`,
|
||||
lastModified: competitorLastModified(competitor),
|
||||
})),
|
||||
]
|
||||
|
||||
return [
|
||||
...staticPages,
|
||||
...blogPages,
|
||||
...authorPages,
|
||||
...libraryPages,
|
||||
...libraryAuthorPages,
|
||||
...integrationPages,
|
||||
...providerPages,
|
||||
...modelEntries,
|
||||
...comparisonPages,
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user