@bernierllc/content-type-blog-post
Version:
Blog post content type with rich TipTap editor, SEO metadata, database storage, and web publishing
65 lines (63 loc) • 2.78 kB
JavaScript
"use strict";
/*
Copyright (c) 2025 Bernier LLC
This file is licensed to the client under a limited-use license.
The client may use and modify this code *only within the scope of the project it was delivered for*.
Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.BlogPostMetadataSchema = exports.BlogPostStatusSchema = exports.BlogPostAuthorSchema = exports.BlogPostSEOSchema = void 0;
const zod_1 = require("zod");
/**
* Blog post SEO metadata schema
* Note: Allows empty strings for draft posts. Use validateSEOCompleteness() to enforce completeness before publishing.
*/
exports.BlogPostSEOSchema = zod_1.z.object({
metaTitle: zod_1.z.string().max(60, 'Meta title should be 60 characters or less'),
metaDescription: zod_1.z.string().max(160, 'Meta description should be 160 characters or less'),
keywords: zod_1.z.array(zod_1.z.string()).max(10, 'Maximum 10 keywords recommended'),
ogImage: zod_1.z.string().url().optional(),
ogType: zod_1.z.literal('article').default('article'),
canonicalUrl: zod_1.z.string().url().optional(),
});
/**
* Blog post author metadata schema
*/
exports.BlogPostAuthorSchema = zod_1.z.object({
name: zod_1.z.string().min(1),
email: zod_1.z.string().email().optional(),
avatar: zod_1.z.string().url().optional(),
bio: zod_1.z.string().max(500).optional(),
});
/**
* Blog post workflow status
*/
exports.BlogPostStatusSchema = zod_1.z.enum(['draft', 'published', 'scheduled', 'archived']);
/**
* Blog post metadata schema (extends text content metadata)
*/
exports.BlogPostMetadataSchema = zod_1.z.object({
// Inherited from TextContentMetadata
content: zod_1.z.string().min(1, 'Content is required'),
createdAt: zod_1.z.date(),
updatedAt: zod_1.z.date(),
// Blog-specific fields
title: zod_1.z.string().min(1).max(200),
slug: zod_1.z.string().min(1).regex(/^[a-z0-9-]+$/, 'Slug must be lowercase letters, numbers, and hyphens'),
excerpt: zod_1.z.string().max(300).optional(),
// SEO metadata
seo: exports.BlogPostSEOSchema,
// Author information
author: exports.BlogPostAuthorSchema,
// Taxonomy
tags: zod_1.z.array(zod_1.z.string()).default([]),
categories: zod_1.z.array(zod_1.z.string()).default([]),
// Publishing workflow
status: exports.BlogPostStatusSchema.default('draft'),
publishedAt: zod_1.z.date().optional(),
scheduledFor: zod_1.z.date().optional(),
// Additional metadata
featuredImage: zod_1.z.string().url().optional(),
readingTime: zod_1.z.number().positive().optional(), // minutes
wordCount: zod_1.z.number().nonnegative().default(0),
});