@edgento/authoring
v0.6.1
Published
Authoring tools for Edgento CMS - validation, inference, manifest generation, and preview handlers
Downloads
366
Maintainers
Readme
@edgento/authoring
⚠️ Internal package. Not intended for direct consumption. API stability is NOT guaranteed. Use
@edgento/sdkand@edgento/cliinstead.
Editor and CLI tooling for Edgento CMS. This package provides validation, type inference, manifest generation, and preview handling for the CMS editor and development tools.
Note: This package is for editor/CLI tooling only. It is NOT required in production customer sites.
Installation
npm install @edgento/authoringFeatures
- Field Validation - Validate field values (email, URL, color, dates, etc.)
- Type Inference - Automatically infer field types from prop names
- Manifest Generation - Generate component manifests for the editor
- Preview Handler - Enable live draft previews in Next.js
Validation API
Validate field values against their definitions with detailed error reporting.
Basic Validators
import {
validateEmail,
validateUrl,
validateColor,
validateDate,
validateDateTime,
validateRange,
validateRequired,
validateOption,
validateOptions,
} from '@edgento/authoring';
// Format validators
validateEmail('[email protected]'); // true
validateUrl('https://example.com'); // true
validateColor('#ff0000'); // true
validateColor('#f00'); // true (short form)
validateDate('2025-12-13'); // true
validateDateTime('2025-12-13T10:30:00Z'); // true
// Constraint validators
validateRange(5, 0, 10); // true (5 is between 0 and 10)
validateRequired('value'); // true
validateRequired(''); // false (empty string)
validateRequired(' '); // false (whitespace only)
validateOption('red', ['red', 'blue']); // true
validateOptions(['red', 'blue'], ['red', 'blue', 'green']); // trueField Validation
Validate complete field definitions with structured error reporting:
import { validateField } from '@edgento/authoring';
import type { ValidationResult } from '@edgento/authoring';
const result: ValidationResult = validateField(
{ type: 'email', label: 'Email', required: true },
'invalid-email'
);
if (!result.valid) {
result.errors.forEach(error => {
console.log(`${error.path}: ${error.message} (${error.code})`);
// Output: "Email: Email must be a valid email address (INVALID_EMAIL)"
});
}Nested Field Validation
Validate group and repeater fields with proper path reporting:
// Group field validation
const groupResult = validateField(
{
type: 'group',
label: 'Address',
fields: {
street: { type: 'text', label: 'Street', required: true },
city: { type: 'text', label: 'City', required: true },
},
},
{ street: '123 Main St', city: '' }
);
// Error path: "Address.city"
// Repeater field validation
const repeaterResult = validateField(
{
type: 'repeater',
label: 'Items',
minItems: 1,
maxItems: 5,
fields: {
title: { type: 'text', label: 'Title', required: true },
},
},
[{ title: '' }]
);
// Error path: "Items.0.title"Inference API
Automatically infer field types and labels from prop names.
Basic Inference
import { inferFieldType, inferLabel } from '@edgento/authoring';
// Suffix-based inference
inferFieldType('heroImage'); // 'image'
inferFieldType('profileImg'); // 'image'
inferFieldType('websiteUrl'); // 'url'
inferFieldType('contactEmail'); // 'email'
inferFieldType('brandColor'); // 'color'
inferFieldType('publishDate'); // 'date'
inferFieldType('createdDateTime'); // 'datetime'
inferFieldType('itemCount'); // 'number'
// Prefix-based inference
inferFieldType('isPublished'); // 'boolean'
inferFieldType('hasComments'); // 'boolean'
// Exact match inference
inferFieldType('title'); // 'text'
inferFieldType('description'); // 'textarea'
// Default fallback
inferFieldType('unknownProp'); // 'text'
// Label generation
inferLabel('heroImage'); // 'Hero Image'
inferLabel('isPublished'); // 'Is Published'
inferLabel('backgroundColor'); // 'Background Color'Explicit Type Override
import { getFieldType } from '@edgento/authoring';
// Explicit type takes precedence
getFieldType('heroImage', 'url'); // 'url' (explicit override)
getFieldType('heroImage', undefined); // 'image' (inferred)Custom Inference Rules
import { createInferenceEngine } from '@edgento/authoring';
import type { InferenceRule, InferenceEngine } from '@edgento/authoring';
const customRules: InferenceRule[] = [
{ pattern: /^hero/, type: 'image', priority: 200 },
{ pattern: /Price$/, type: 'number', priority: 150 },
];
const engine: InferenceEngine = createInferenceEngine(customRules);
engine.inferType('heroSection'); // 'image' (custom rule)
engine.inferType('productPrice'); // 'number' (custom rule)
engine.inferType('contactEmail'); // 'email' (default rule)
engine.inferLabel('heroSection'); // 'Hero Section'
engine.getRules(); // All rules sorted by priorityManifest Generation
Generate component manifests for the CMS editor.
import { generateManifest, serializeManifest } from '@edgento/authoring';
import type { ComponentManifest } from '@edgento/authoring';
// Generate manifest from component registrations
const manifest: ComponentManifest = generateManifest([
{
name: 'Hero',
label: 'Hero Section',
category: 'Sections',
description: 'Full-width hero with image and text',
fields: {
title: { type: 'text', label: 'Title', required: true },
backgroundImage: { type: 'image', label: 'Background Image' },
},
},
{
name: 'Card',
label: 'Card',
category: 'Components',
fields: {
title: { type: 'text', label: 'Title' },
description: { type: 'textarea', label: 'Description' },
},
},
]);
// Access manifest data
console.log(manifest.version); // Version for cache invalidation
console.log(manifest.generatedAt); // ISO timestamp
console.log(manifest.components); // All components
console.log(manifest.byCategory); // Components grouped by category
// { Sections: [Hero], Components: [Card] }
// Serialize to JSON
const json: string = serializeManifest(manifest);Preview Handler
Enable live draft previews in Next.js applications.
Setup Preview API Route
// pages/api/preview.ts
import { createPreviewHandler } from '@edgento/authoring';
export default createPreviewHandler({
secret: process.env.PREVIEW_SECRET!,
apiUrl: process.env.CMS_API_URL!,
cookieName: '__edgento_preview', // optional
maxAge: 3600, // optional, in seconds
});Setup Exit Preview Route
// pages/api/exit-preview.ts
import { exitPreview } from '@edgento/authoring';
export default exitPreview();Check Preview Status
import { getPreviewData } from '@edgento/authoring';
import type { PreviewData } from '@edgento/authoring';
// In getServerSideProps or API route
const previewData: PreviewData | null = getPreviewData(
req.cookies,
process.env.PREVIEW_SECRET!
);
if (previewData?.enabled) {
// Fetch draft content
}Fetch Draft Content
import { fetchDraftContent } from '@edgento/authoring';
const draftPage = await fetchDraftContent(
process.env.CMS_API_URL!,
'page',
'home',
previewToken
);
const draftPost = await fetchDraftContent(
process.env.CMS_API_URL!,
'post',
'my-blog-post',
previewToken
);Preview Banner Component
Display a visual indicator when preview mode is active:
import { PreviewBanner } from '@edgento/authoring';
function Layout({ children, isPreview }) {
return (
<>
{isPreview && (
<PreviewBanner
exitUrl="/api/exit-preview"
position="top"
className="custom-banner"
/>
)}
{children}
</>
);
}Token Generation (for CMS backend)
import { generatePreviewToken, validatePreviewToken } from '@edgento/authoring';
// Generate a token (CMS backend)
const expiresAt = Date.now() + 3600 * 1000; // 1 hour
const token = generatePreviewToken(process.env.PREVIEW_SECRET!, expiresAt);
// Validate a token
const result = validatePreviewToken(token, process.env.PREVIEW_SECRET!);
if (result.valid) {
console.log('Token expires at:', new Date(result.expiresAt!));
}Types
All types are exported for TypeScript users:
import type {
// Validation
ValidationResult,
ValidationError,
ValidationErrorCode,
// Inference
InferenceRule,
InferenceEngine,
// Manifest
ComponentManifest,
ManifestComponent,
// Preview
PreviewConfig,
PreviewData,
PreviewBannerProps,
NextApiHandler,
} from '@edgento/authoring';License
MIT
