Rustelo/resources/nickel/site/contracts.ncl

309 lines
6.5 KiB
Text
Raw Normal View History

2026-07-18 20:16:16 +01:00
# Unified Site Configuration Contracts
#
# Type contracts for site.ncl — validates the complete site configuration
# including site metadata, paths, themes, content types, and routes with
# embedded multilingual menu data.
#
# JSON output of site.ncl is deserialized by build_config::site_config::UnifiedSiteConfig.
{
SiteMetadata = {
name
| String
| doc "Site display name",
languages
| Array String
| doc "Supported language codes (ISO 639-1, e.g. ['en', 'es'])",
default_language
| String
| doc "Default language code when none is specified in URL",
},
SitePaths = {
i18n
| String
| doc "Path to i18n/FTL locales directory",
content
| String
| doc "Path to content directory",
themes
| String
| doc "Path to themes configuration directory",
assets
| String
| doc "Path to public assets directory",
migrations
| String
| doc "Path to SQL migration files directory (server-only)"
| optional,
email_templates
| String
| doc "Path to email template files directory (server-only)"
| optional,
work
| String
| doc "Path to work-in-progress / staging assets directory (implementation-specific)"
| optional,
},
ThemeConfig = {
default
| String
| doc "Default theme name",
available
| Array String
| doc "List of available theme names",
},
ContentTypeFeatures = {
style_mode
| String
| doc "Display style: 'row' or 'grid'",
use_feature
| Bool
| doc "Enable featured content at top of index",
use_categories
| Bool
| default = true,
use_tags
| Bool
| default = true,
use_emojis
| Bool
| default = false,
cta_view
| String
| doc "CTA component name (e.g. 'BlogCTAView')",
default_page_size
| Number
| default = 10,
# Allow additional feature fields without breaking the contract
..
},
ContentType = {
name
| String
| doc "Content type identifier (e.g. 'blog', 'recipes')",
directory
| String
| doc "Directory name under content path",
enabled
| Bool
| default = true,
features
| ContentTypeFeatures
| doc "Content display and pagination settings",
},
# Database connection configuration.
# Structural settings (type, path, pool sizing) belong here.
# Credentials and full overrides go in .env via DATABASE_URL.
DatabaseConfig = {
url
| String
| doc "Database connection URL (e.g. 'sqlite:data/dev.db' or 'postgres://...')",
create_if_missing
| Bool
| doc "Create the SQLite database file if it does not exist"
| default = true,
max_connections
| Number
| doc "Maximum number of connections in the pool"
| default = 5,
min_connections
| Number
| doc "Minimum number of idle connections in the pool"
| default = 1,
connect_timeout
| Number
| doc "Connection acquisition timeout in seconds"
| default = 30,
idle_timeout
| Number
| doc "Maximum idle connection duration in seconds"
| default = 600,
max_lifetime
| Number
| doc "Maximum connection lifetime in seconds"
| default = 1800,
},
# Footer metadata — absorbed inline into SiteConfig.
FooterMetadata = {
title
| String
| doc "Footer title (language-neutral, e.g. author name)",
company_desc
| { _ : String }
| doc "Company description keyed by language code",
copyright_text
| { _ : String }
| doc "Copyright notice keyed by language code",
social_links
| Array {
name | String,
url | String,
icon | String,
}
| default = [],
},
# Menu configuration embedded directly in Route.
# At least one of label_key or labels must be present (validated by Nickel contracts
# plus runtime assertion in Rust loader).
MenuEntry = {
enabled
| Bool
| doc "Whether this route appears in navigation"
| default = false,
order
| Number
| doc "Display order (lower = first)"
| optional,
icon
| String
| doc "Icon identifier (e.g. 'home', 'fas fa-user')"
| optional,
label_key
| String
| doc "Primary: FTL i18n key for label (e.g. 'nav-home')"
| optional,
labels
| { _ : String }
| doc "Fallback inline labels keyed by language code"
| optional,
visible_in
| Array String
| doc "Language codes this item is visible in. Empty = all languages."
| default = [],
use_in
| Array String
| doc "Navigation zones: ['header'], ['footer'], or ['header','footer']"
| default = ["header"],
},
# A unified route entry covering all languages in a single record.
# paths maps language code → URL path (e.g. { en = '/', es = '/inicio' }).
Route = {
component
| String
| doc "Page component name (e.g. 'HomePage', 'ContentIndex')",
paths
| { _ : String }
| doc "URL paths keyed by language code",
priority
| Number
| doc "SEO/sitemap priority (0.01.0)"
| default = 0.8,
content_type
| String
| doc "Content type for dynamic routes (e.g. 'blog')"
| optional,
menu
| MenuEntry
| doc "Navigation menu configuration for this route",
props
| { _ : Dyn }
| doc "Additional route-specific properties passed to component"
| optional,
},
LogoConfig = {
light_src
| String
| doc "Path to light-mode logo image",
dark_src
| String
| doc "Path to dark-mode logo image (empty string disables dark variant)",
alt
| String
| doc "Alt text for the logo image",
show_in_nav
| Bool
| doc "Whether to show the logo in the navigation bar",
show_in_footer
| Bool
| doc "Whether to show the logo in the footer",
width
| optional
| doc "Optional logo width (CSS value or null)",
height
| optional
| doc "Optional logo height (CSS value or null)",
},
SiteConfig = {
site
| SiteMetadata,
paths
| SitePaths,
themes
| ThemeConfig,
logo
| LogoConfig,
content_types
| Array ContentType,
database
| DatabaseConfig
| doc "Database connection and pool configuration",
footer
| FooterMetadata,
routes
| Array Route,
},
}