From 6ab157fa52aa9a30a1fb65ff7c6553afefca384d Mon Sep 17 00:00:00 2001 From: Willow (GHOST) Date: Tue, 10 Mar 2026 07:01:22 +0000 Subject: [PATCH] feat: add updated at support --- sequoia.schema.json | 5 +++++ packages/cli/src/lib/atproto.ts | 23 ++++++++++++++++++++++- packages/cli/src/lib/markdown.ts | 17 +++++++++++++++++ packages/cli/src/lib/types.ts | 2 ++ 4 file(s) changed, 46 insertion(s)(+), 1 deletion(s)(-) diff --git a/sequoia.schema.json b/sequoia.schema.json --- a/sequoia.schema.json +++ b/sequoia.schema.json @@ -70,6 +70,11 @@ "description": "Field name for publish date (checks \"publishDate\", \"pubDate\", \"date\", \"createdAt\", and \"created_at\" by default)", "default": "publishDate" }, + "updatedAt": { + "type": "string", + "description": "Field name for updated date (checks \"updatedAt\", \"updated_at\", \"modifiedAt\", and \"modified_at\" by default)", + "default": "updatedAt" + }, "coverImage": { "type": "string", "description": "Field name for cover image", diff --git a/packages/cli/src/lib/atproto.ts b/packages/cli/src/lib/atproto.ts --- a/packages/cli/src/lib/atproto.ts +++ b/packages/cli/src/lib/atproto.ts @@ -26,7 +26,8 @@ typeof v.site === "string" && typeof v.path === "string" && typeof v.textContent === "string" && - typeof v.publishedAt === "string" + typeof v.publishedAt === "string" && + (v.updatedAt === undefined || typeof v.updatedAt === "string") ); } @@ -252,6 +253,12 @@ ); const publishDate = new Date(post.frontmatter.publishDate); + // Handle updatedAt: only set if explicitly provided in frontmatter + let updatedAt: Date | undefined; + if (post.frontmatter.updatedAt) { + updatedAt = new Date(post.frontmatter.updatedAt); + } + // Determine textContent (if enabled): use configured field from frontmatter, or fallback to markdown body let textContent: string | null = null; if ( @@ -273,6 +280,10 @@ publishedAt: publishDate.toISOString(), canonicalUrl: `${config.siteUrl}${postPath}`, }; + + if (updatedAt) { + record.updatedAt = updatedAt.toISOString(); + } if (post.frontmatter.description) { record.description = post.frontmatter.description; @@ -318,6 +329,11 @@ ); const publishDate = new Date(post.frontmatter.publishDate); + // Handle updatedAt: only set if explicitly provided in frontmatter + let updatedAt = post.frontmatter.updatedAt + ? new Date(post.frontmatter.updatedAt) + : undefined; + // Determine textContent (if enabled): use configured field from frontmatter, or fallback to markdown body let textContent: string | null = null; if ( @@ -348,6 +364,10 @@ publishedAt: publishDate.toISOString(), canonicalUrl: `${config.siteUrl}${postPath}`, }; + + if (updatedAt) { + record.updatedAt = updatedAt.toISOString(); + } if (post.frontmatter.description) { record.description = post.frontmatter.description; @@ -388,6 +408,7 @@ path: string; textContent?: string; publishedAt: string; + updatedAt?: string; canonicalUrl?: string; description?: string; coverImage?: BlobObject; diff --git a/packages/cli/src/lib/markdown.ts b/packages/cli/src/lib/markdown.ts --- a/packages/cli/src/lib/markdown.ts +++ b/packages/cli/src/lib/markdown.ts @@ -75,6 +75,23 @@ } } + // Updated date mapping - check custom field first, then fallbacks + const updatedAtField = mapping?.updatedAt; + if (updatedAtField && raw[updatedAtField]) { + frontmatter.updatedAt = raw[updatedAtField]; + } else if (raw.updatedAt) { + frontmatter.updatedAt = raw.updatedAt; + } else { + // Fallback to common date field names + const updatedAtFields = ["updated_at", "modifiedAt", "modified_at"]; + for (const field of updatedAtFields) { + if (raw[field]) { + frontmatter.updatedAt = raw[field]; + break; + } + } + } + // Cover image mapping const coverField = mapping?.coverImage || "ogImage"; frontmatter.ogImage = raw[coverField] || raw.ogImage; diff --git a/packages/cli/src/lib/types.ts b/packages/cli/src/lib/types.ts --- a/packages/cli/src/lib/types.ts +++ b/packages/cli/src/lib/types.ts @@ -2,6 +2,7 @@ title?: string; // Field name for title (default: "title") description?: string; // Field name for description (default: "description") publishDate?: string; // Field name for publish date (default: "publishDate", also checks "pubDate", "date", "createdAt", "created_at") + updatedAt?: string; // Field name for updated date (default: "updatedAt", also checks "updated_at", "modifiedAt", "modified_at") coverImage?: string; // Field name for cover image (default: "ogImage") tags?: string; // Field name for tags (default: "tags") draft?: string; // Field name for draft status (default: "draft") @@ -90,6 +91,7 @@ description?: string; bskyPost?: string; publishDate: string; + updatedAt?: string; tags?: string[]; ogImage?: string; atUri?: string; -- tangled.sh